Skip to content

Commit

Permalink
feature: Add attribute fileName (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
wndhydrnt committed Mar 28, 2024
1 parent ad071f9 commit 49ca421
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 17 deletions.
23 changes: 13 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,14 @@ Create a `SecretProviderClass` resource to provide Spring-Cloud-Config-specific
apiVersion: secrets-store.csi.x-k8s.io/v1alpha1
kind: SecretProviderClass
metadata:
name: spring-cloud-config-<your-application>
name: spring-cloud-config-example
spec:
provider: spring-cloud-config
parameters:
serverAddress: "<your-server-address>" # this url should point config server
application: "<your-application>" # the application you're retrieving the config for
profile: "<your-profile>" # the profile for your application to pull
fileType: "json" # json or properties viable

serverAddress: "http://configserver.example" # this url should point to config server
application: "myapp" # the application you're retrieving the config for
profile: "prod" # the profile for your application to pull
fileName: "application.yaml" # the name of the file to create - supports extensions .yaml, .yml, .json and .properties
```

Afterwards you can reference your `SecretProviderClass` in your Pod Definition
Expand All @@ -42,11 +41,15 @@ Afterwards you can reference your `SecretProviderClass` in your Pod Definition
kind: Pod
apiVersion: v1
metadata:
name: nginx-secrets-store-inline
name: secrets-store-example
spec:
containers:
- image: nginx
name: nginx
- image: ubuntu:latest
name: ubuntu
command: ["/bin/bash"]
args:
- "-c"
- "cat /secrets-store/application.yaml && sleep 300"
volumeMounts:
- name: secrets-store-inline
mountPath: "/secrets-store"
Expand All @@ -57,7 +60,7 @@ spec:
driver: secrets-store.csi.k8s.com
readOnly: true
volumeAttributes:
secretProviderClass: "spring-cloud-config-<your-application>"
secretProviderClass: "spring-cloud-config-example"
```

## Release
Expand Down
2 changes: 1 addition & 1 deletion pkg/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func NewSpringCloudConfigClient(c *http.Client, retryBaseWait time.Duration, ret
// GetConfig pulls the config from spring-cloud config server and the server parses it to the specified format
// if the config contains secrets they are decoded on the server side
func (c *SpringCloudConfigClient) GetConfig(attributes Attributes) (io.ReadCloser, error) {
fullAddress := attributes.ServerAddress + springGetConfigPath + attributes.Application + "/" + attributes.Profile + "." + attributes.FileType
fullAddress := attributes.ServerAddress + springGetConfigPath + attributes.Application + "/" + attributes.Profile + attributes.extension()
req, err := http.NewRequest("GET", fullAddress, nil)
if err != nil {
return nil, fmt.Errorf("create new GetConfig request: %w", err)
Expand Down
41 changes: 36 additions & 5 deletions pkg/provider/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"net/http"
"os"
"path"
"slices"
"strings"
"time"

log "github.com/sirupsen/logrus"
Expand All @@ -17,6 +19,10 @@ import (
"sigs.k8s.io/secrets-store-csi-driver/provider/v1alpha1"
)

var (
supportedFileExtensions = []string{".json", ".properties", ".yaml", ".yml"}
)

type SpringCloudConfigCSIProviderServer struct {
grpcServer *grpc.Server
listener net.Listener
Expand All @@ -30,6 +36,7 @@ type Attributes struct {
ServerAddress string `json:"serverAddress,omitempty"`
Application string `json:"application,omitempty"`
Profile string `json:"profile,omitempty"`
FileName string `json:"fileName,omitempty"`
FileType string `json:"fileType,omitempty"`
Raw string `json:"raw"`
}
Expand Down Expand Up @@ -78,14 +85,32 @@ func (a *Attributes) verify() (err error) {
return fmt.Errorf("profile is not set")
}

// TODO might want to warn/info in-case only raw files were created
if a.FileType == "" && len(raw) == 0 {
return fmt.Errorf("FileType and raw are not set, atleast one is required")
if a.FileType == "" && len(raw) == 0 && a.FileName == "" {
return fmt.Errorf("attributes fileName, fileType or raw are not set, at least one is required")
}

if a.FileType != "" {
log.Warnf("%s/%s specifies deprecated attribute fileType - should use fileName instead", a.Application, a.Profile)
}

if a.FileName != "" {
ext := path.Ext(a.FileName)
if !slices.Contains(supportedFileExtensions, ext) {
return fmt.Errorf("fileName %s uses an unsupported extension - supported extensions are %s", a.FileName, strings.Join(supportedFileExtensions, ","))
}
}

return nil
}

func (a *Attributes) extension() string {
if a.FileName != "" {
return path.Ext(a.FileName)
}

return "." + a.FileType
}

// NewSpringCloudConfigCSIProviderServer returns CSI provider that uses the spring as the secret backend
func NewSpringCloudConfigCSIProviderServer(socketPath string, httpClient *http.Client, retryBaseWait time.Duration, retryMax uint64) (*SpringCloudConfigCSIProviderServer, error) {
client := NewSpringCloudConfigClient(httpClient, retryBaseWait, retryMax)
Expand Down Expand Up @@ -158,7 +183,7 @@ func (m *SpringCloudConfigCSIProviderServer) Mount(ctx context.Context, req *v1a
},
}

if attrib.FileType != "" {
if attrib.FileType != "" || attrib.FileName != "" {
err = m.mountFile(attrib, req.GetTargetPath(), filePermission)
if err != nil {
return nil, err
Expand Down Expand Up @@ -189,7 +214,13 @@ func (m *SpringCloudConfigCSIProviderServer) Version(ctx context.Context, req *v
}, nil
}
func (m *SpringCloudConfigCSIProviderServer) mountFile(attrib Attributes, targetPath string, filePermission os.FileMode) error {
fileName := fmt.Sprintf("%s-%s.%s", attrib.Application, attrib.Profile, attrib.FileType)
var fileName string
if attrib.FileName == "" {
fileName = fmt.Sprintf("%s-%s.%s", attrib.Application, attrib.Profile, attrib.FileType)
} else {
fileName = attrib.FileName
}

content, err := m.springCloudConfigClient.GetConfig(attrib)
if err != nil {
return fmt.Errorf("failed to retrieve secrets for %s: %w", fileName, err)
Expand Down
47 changes: 46 additions & 1 deletion pkg/provider/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,51 @@ func TestSpringCloudConfigCSIProviderServer_Mount(t *testing.T) {
},
wantFiles: map[string]string{"some-testing.json": `{"some":"json"}`},
},
{
name: "When attribute `FileName` is set then it uses the attribute to create the file",
configServerRequests: []*configServerRequest{
{
path: "/config/some/testing.json",
statusCode: 200,
responsePayload: `{"some":"json"}`,
},
},
attrib: Attributes{
ServerAddress: "http://configserver.localhost",
Profile: "testing",
Application: "some",
FileName: "config.json",
},
wantFiles: map[string]string{"config.json": `{"some":"json"}`},
},
{
name: "When both attributes `FileName` and `FileType` are set then attribute `FileName` takes precedence",
configServerRequests: []*configServerRequest{
{
path: "/config/some/testing.json",
statusCode: 200,
responsePayload: `{"some":"json"}`,
},
},
attrib: Attributes{
ServerAddress: "http://configserver.localhost",
Profile: "testing",
Application: "some",
FileType: "json",
FileName: "config.json",
},
wantFiles: map[string]string{"config.json": `{"some":"json"}`},
},
{
name: "When attribute `FileName` defines an unsupported extension then it errors",
attrib: Attributes{
ServerAddress: "http://configserver.localhost",
Profile: "testing",
Application: "some",
FileName: "config.toml",
},
wantError: errors.New("fileName config.toml uses an unsupported extension - supported extensions are .json,.properties,.yaml,.yml"),
},
{
name: "When raw files are part of the attributes then it creates the raw files",
configServerRequests: []*configServerRequest{
Expand All @@ -74,7 +119,7 @@ func TestSpringCloudConfigCSIProviderServer_Mount(t *testing.T) {
Profile: "testing",
Application: "some",
},
wantError: errors.New("FileType and raw are not set, atleast one is required"),
wantError: errors.New("attributes fileName, fileType or raw are not set, at least one is required"),
},
{
name: "When ConfigServer returns an error then it errors",
Expand Down

0 comments on commit 49ca421

Please sign in to comment.