Skip to content

Commit

Permalink
fix versioning broken when deploying to multiple clusters with same a…
Browse files Browse the repository at this point in the history
…rtifact (#169)

* fix versioning broken when deploying to multiple clusters with same artifact

Co-authored-by: abe garcia <[email protected]>
  • Loading branch information
alice485 and abe garcia authored Sep 6, 2023
1 parent 664330e commit 45641be
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 26 deletions.
2 changes: 1 addition & 1 deletion internal/api/core/kubernetes/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func (cc *Controller) Deploy(c *gin.Context, dm DeployManifestRequest) {
return
}

kubernetes.BindArtifacts(&manifest, artifacts)
kubernetes.BindArtifacts(&manifest, artifacts, dm.Account)

if kubernetes.IsVersioned(manifest) {
err := handleVersionedManifest(provider.Client, &manifest, application)
Expand Down
2 changes: 1 addition & 1 deletion internal/api/core/kubernetes/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (cc *Controller) Patch(c *gin.Context, pm PatchManifestRequest) {
u := unstructured.Unstructured{
Object: m,
}
kubernetes.BindArtifacts(&u, pm.AllArtifacts)
kubernetes.BindArtifacts(&u, pm.AllArtifacts, pm.Account)

b, err = json.Marshal(&u.Object)
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions internal/api/core/kubernetes/patch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ var _ = Describe("Patch", func() {
Reference: "gcr.io/test-project/test-container-image:v1.0.0",
Name: "gcr.io/test-project/test-container-image",
Type: artifact.TypeDockerImage,
Metadata: clouddriver.ArtifactMetadata{Account: patchManifestRequest.Account},
},
}
})
Expand Down
2 changes: 1 addition & 1 deletion internal/api/core/kubernetes/runjob.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (cc *Controller) RunJob(c *gin.Context, rj RunJobRequest) {
u.SetName(generateName + rand.String(randNameNumber))
}

kubernetes.BindArtifacts(&u, append(rj.RequiredArtifacts, rj.OptionalArtifacts...))
kubernetes.BindArtifacts(&u, append(rj.RequiredArtifacts, rj.OptionalArtifacts...), rj.Account)

meta := kubernetes.Metadata{}
if kubernetes.Replace(u) {
Expand Down
1 change: 1 addition & 0 deletions internal/api/core/kubernetes/runjob_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ var _ = Describe("RunJob", func() {
Reference: "gcr.io/test-project/test-container-image:v1.0.0",
Name: "gcr.io/test-project/test-container-image",
Type: artifact.TypeDockerImage,
Metadata: clouddriver.ArtifactMetadata{Account: runJobRequest.Account},
},
}
})
Expand Down
58 changes: 36 additions & 22 deletions internal/kubernetes/artifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,36 +53,44 @@ var (
// apiVersion: v1
// kind: Pod
// metadata:
// name: dapi-test-pod
//
// name: dapi-test-pod
//
// spec:
// containers:
// - name: test-container
// image: k8s.gcr.io/busybox
// volumeMounts:
// - name: my-volume
// mountPath: /etc/config
// volumes:
// - name: my-volume
// configMap:
// # Provide the name of the ConfigMap containing the files you want
// # to add to the container
// name: replace-me
// restartPolicy: Never
//
// containers:
// - name: test-container
// image: k8s.gcr.io/busybox
// volumeMounts:
// - name: my-volume
// mountPath: /etc/config
// volumes:
// - name: my-volume
// configMap:
// # Provide the name of the ConfigMap containing the files you want
// # to add to the container
// name: replace-me
// restartPolicy: Never
//
// Now let's say we pass in the following Clouddriver Artifact:
// {
// "name": "replace-me",
// "reference": "my-config-map-v000"
// }
//
// {
// "name": "replace-me",
// "reference": "my-config-map-v000"
// }
//
// This would result in the JSON path '.spec.volumes[0].configMap.name' changing from
// 'replace-me' to 'my-config-map-v000'.
//
// The source code for these Replacers can be found here:
// https://github.com/spinnaker/clouddriver/blob/4d4e01084ac5259792020e419b1af7686ab38019/clouddriver-kubernetes/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/artifact/Replacer.java#L150
func BindArtifacts(u *unstructured.Unstructured,
artifacts []clouddriver.Artifact) {
artifacts []clouddriver.Artifact, account string) {
for _, a := range artifacts {
if !isBindable(a, account) {
continue
}

switch a.Type {
case artifact.TypeDockerImage:
bindArtifact(u.Object, a, iterables(jsonPathDockerImageContainers)...)
Expand Down Expand Up @@ -147,9 +155,11 @@ func BindArtifacts(u *unstructured.Unstructured,
//
// Take for example the following variadic arguments for paths passed into the function:
// [
// '.spec.containers',
// '.env',
// '.field.name'
//
// '.spec.containers',
// '.env',
// '.field.name'
//
// ]
//
// In this case the first two args ('.spec.containers' and '.env') are of type []interface{}.
Expand Down Expand Up @@ -181,6 +191,10 @@ func bindArtifact(obj map[string]interface{}, a clouddriver.Artifact, paths ...s
}
}

func isBindable(artifact clouddriver.Artifact, account string) bool {
return artifact.Metadata.Account == account
}

// FindArtifacts lists all artifacts found in a given manifest.
// It is expected to be used only for Kubernetes kind Deployment, so non-Deployment kind
// paths are omitted.
Expand Down
69 changes: 68 additions & 1 deletion internal/kubernetes/artifact_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,50 +16,117 @@ var _ = Describe("Artifact", func() {
var (
resource *unstructured.Unstructured
artifacts []clouddriver.Artifact
account string
)

BeforeEach(func() {
account = "my-test-acct"
artifacts = []clouddriver.Artifact{
{
Name: "gcr.io/test-project/test-container-image",
Type: artifact.TypeDockerImage,
Reference: "gcr.io/test-project/test-container-image:v1.0.0",
Metadata: clouddriver.ArtifactMetadata{Account: account},
},
{
Name: "my-config-map",
Type: artifact.TypeKubernetesConfigMap,
Reference: "my-config-map-v000",
Metadata: clouddriver.ArtifactMetadata{Account: account},
},
{
Name: "my-config-map2",
Type: artifact.TypeKubernetesConfigMap,
Reference: "my-config-map2-v000",
Metadata: clouddriver.ArtifactMetadata{Account: account},
},
{
Name: "my-secret",
Type: artifact.TypeKubernetesSecret,
Reference: "my-secret-v000",
Metadata: clouddriver.ArtifactMetadata{Account: account},
},
{
Name: "my-secret2",
Type: artifact.TypeKubernetesSecret,
Reference: "my-secret2-v000",
Metadata: clouddriver.ArtifactMetadata{Account: account},
},
{
Name: "my-deployment",
Type: artifact.TypeKubernetesDeployment,
Reference: "my-deployment-v000",
Metadata: clouddriver.ArtifactMetadata{Account: account},
},
{
Name: "my-replicaSet",
Type: artifact.TypeKubernetesReplicaSet,
Reference: "my-replicaSet-v000",
Metadata: clouddriver.ArtifactMetadata{Account: account},
},
}
})

JustBeforeEach(func() {
BindArtifacts(resource, artifacts)
BindArtifacts(resource, artifacts, account)
})

When("an artifact that does not exist in the target cluster is present in the execution context", func() {
BeforeEach(func() {
account = "my-test-acct"
artifacts = []clouddriver.Artifact{
{
Name: "my-config-map",
Type: artifact.TypeKubernetesConfigMap,
Reference: "my-config-map-v001",
Metadata: clouddriver.ArtifactMetadata{Account: "my-test-acct-2"},
},
{
Name: "my-config-map",
Type: artifact.TypeKubernetesConfigMap,
Reference: "my-config-map-v000",
Metadata: clouddriver.ArtifactMetadata{Account: account},
},
{
Name: "my-config-map2",
Type: artifact.TypeKubernetesConfigMap,
Reference: "my-config-map2-v000",
Metadata: clouddriver.ArtifactMetadata{Account: account},
},
}
resource = &unstructured.Unstructured{
Object: map[string]interface{}{
"kind": "Deployment",
"apiVersion": "apps/v1",
"spec": map[string]interface{}{
"template": map[string]interface{}{
"spec": map[string]interface{}{
"volumes": []interface{}{
map[string]interface{}{
"configMap": map[string]interface{}{
"name": "not-my-config-map",
},
},
map[string]interface{}{
"configMap": map[string]interface{}{
"name": "my-config-map",
},
},
},
},
},
},
},
}
})

It("ignores the artifact corresponding to the wrong target cluster", func() {
o := NewDeployment(resource.Object)
volumes := o.Object().Spec.Template.Spec.Volumes
Expect(volumes).To(HaveLen(2))
Expect(volumes[0].VolumeSource.ConfigMap.Name).To(Equal("not-my-config-map"))
Expect(volumes[1].VolumeSource.ConfigMap.Name).To(Equal("my-config-map-v000"))
})
})

When("the iterable path is not of type []interface{}", func() {
Expand Down

0 comments on commit 45641be

Please sign in to comment.