diff --git a/Dockerfile b/Dockerfile index 6493d29..e522505 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,7 @@ COPY util/ util/ COPY web/ web/ COPY swarmcd/ swarmcd/ RUN CGO_ENABLED=0 GOOS=linux go build -o /swarm-cd ./cmd/ +RUN go test ./swarmcd/ # Stage 3: Final production image (depends on previous stages) FROM alpine:3.2 diff --git a/swarmcd/stack.go b/swarmcd/stack.go index 2b11584..846cfdc 100644 --- a/swarmcd/stack.go +++ b/swarmcd/stack.go @@ -157,6 +157,10 @@ func discoverSecrets(composeMap map[string]any, composePath string) ([]string, e if !ok { return nil, fmt.Errorf("invalid compose file: %s secret must be a map", secretName) } + isExternal, ok := secretMap["external"].(bool) + if ok && isExternal { + continue + } secretFile, ok := secretMap["file"].(string) if !ok { return nil, fmt.Errorf("invalid compose file: %s file field must be a string", secretName) @@ -191,6 +195,10 @@ func (swarmStack *swarmStack) rotateObjects(objects map[string]any) error { if !ok { return fmt.Errorf("invalid compose file: %s object must be a map", objectName) } + isExternal, ok := objectMap["external"].(bool) + if ok && isExternal { + continue + } objectFile, ok := objectMap["file"].(string) if !ok { return fmt.Errorf("invalid compose file: %s file field must be a string", objectName) diff --git a/swarmcd/stack_test.go b/swarmcd/stack_test.go new file mode 100644 index 0000000..aa280f8 --- /dev/null +++ b/swarmcd/stack_test.go @@ -0,0 +1,50 @@ +package swarmcd + +import ( + "sync" + "testing" +) + +// External objects are ignored by the rotation +func TestRotateExternalObjects(t *testing.T) { + repo := &stackRepo{name: "test", path: "test", url: "", auth: nil, lock: &sync.Mutex{}, gitRepoObject: nil} + stack := newSwarmStack("test", repo, "main", "docker-compose.yaml", nil, "", false) + objects := map[string]any{ + "my-secret": map[string]any{"external": true}, + } + err := stack.rotateObjects(objects) + if err != nil { + t.Errorf("unexpected error: %s", err) + } +} + +// Secrets are discovered, external secrets are ignored +func TestSecretDiscovery(t *testing.T) { + repo := &stackRepo{name: "test", path: "test", url: "", auth: nil, lock: &sync.Mutex{}, gitRepoObject: nil} + stack := newSwarmStack("test", repo, "main", "stacks/docker-compose.yaml", nil, "", false) + stackString := []byte(`services: + my-service: + image: my-image + secrets: + - my-secret + - my-external-secret +secrets: + my-secret: + file: secrets/secret.yaml + my-external-secret: + external: true`) + composeMap, err := stack.parseStackString(stackString) + if err != nil { + t.Errorf("unexpected error: %s", err) + } + sopsFiles, err := discoverSecrets(composeMap, stack.composePath) + if err != nil { + t.Errorf("unexpected error: %s", err) + } + if len(sopsFiles) != 1 { + t.Errorf("unexpected number of sops files: %d", len(sopsFiles)) + } + if sopsFiles[0] != "stacks/secrets/secret.yaml" { + t.Errorf("unexpected sops file: %s", sopsFiles[0]) + } +}