From aafeb75ef1dd9c6a5da92ba10c477b557382efeb Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Fri, 20 Dec 2019 03:43:11 -0800 Subject: [PATCH] Rebase merge3 branch into master --- cmd/config/configcobra/cmds.go | 1 + cmd/config/docs/commands/merge3.md | 25 +++ cmd/config/internal/commands/merge3.go | 56 +++++ cmd/config/internal/commands/merge3_test.go | 236 ++++++++++++++++++++ 4 files changed, 318 insertions(+) create mode 100644 cmd/config/docs/commands/merge3.md create mode 100644 cmd/config/internal/commands/merge3.go create mode 100644 cmd/config/internal/commands/merge3_test.go diff --git a/cmd/config/configcobra/cmds.go b/cmd/config/configcobra/cmds.go index 0fd9c86bd..3e1126e1d 100644 --- a/cmd/config/configcobra/cmds.go +++ b/cmd/config/configcobra/cmds.go @@ -74,6 +74,7 @@ func NewConfigCommand(name string) *cobra.Command { root.AddCommand(commands.CatCommand(name)) root.AddCommand(commands.FmtCommand(name)) root.AddCommand(commands.MergeCommand(name)) + root.AddCommand(commands.Merge3Command(name)) root.AddCommand(commands.CountCommand(name)) root.AddCommand(commands.RunFnCommand(name)) root.AddCommand(commands.SubCommand(name)) diff --git a/cmd/config/docs/commands/merge3.md b/cmd/config/docs/commands/merge3.md new file mode 100644 index 000000000..7991e5290 --- /dev/null +++ b/cmd/config/docs/commands/merge3.md @@ -0,0 +1,25 @@ +## merge3 + +[Alpha] Merge Resource configuration files (3-way) + +### Synopsis + +[Alpha] Merge Resource configuration files (3-way) + +Merge (3-way) reads Kubernetes Resource yaml configuration files from source packages and updated +packages then writes the result to stdout and a destination package. + +Resources are merged using the Resource [apiVersion, kind, name, namespace] as the key. If any of +these are missing, merge will default the missing values to empty. + +Resources specified in the updated packages have higher-precedence and Resources specified +in the original packages have lower-precedence. Resources specified in the destination +packages either keep, clear, or recursively merge their values. + +For information on merge rules, run: + + kustomize config docs merge3 + +### Examples + + kustomize config merge3 --ancestor a/ --from b/ --to c/ \ No newline at end of file diff --git a/cmd/config/internal/commands/merge3.go b/cmd/config/internal/commands/merge3.go new file mode 100644 index 000000000..6ef234a35 --- /dev/null +++ b/cmd/config/internal/commands/merge3.go @@ -0,0 +1,56 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package commands + +import ( + "github.com/spf13/cobra" + "sigs.k8s.io/kustomize/cmd/config/cmddocs/commands" + "sigs.k8s.io/kustomize/kyaml/kio/filters" +) + +func GetMerge3Runner(name string) *Merge3Runner { + r := &Merge3Runner{} + c := &cobra.Command{ + Use: "merge3 --ancestor [ORIGINAL_DIR] --from [UPDATED_DIR] --to [DESTINATION_DIR]", + Short: commands.Merge3Short, + Long: commands.Merge3Long, + Example: commands.Merge3Examples, + RunE: r.runE, + } + fixDocs(name, c) + //r.Command = c + c.Flags().StringVar(&r.ancestor, "ancestor", "", + "Path to original package") + c.Flags().StringVar(&r.fromDir, "from", "", + "Path to updated package") + c.Flags().StringVar(&r.toDir, "to", "", + "Path to destination package") + + r.Command = c + return r +} + +func Merge3Command(name string) *cobra.Command { + return GetMerge3Runner(name).Command +} + +// Merge3Runner contains the run function +type Merge3Runner struct { + Command *cobra.Command + ancestor string + fromDir string + toDir string +} + +func (r *Merge3Runner) runE(c *cobra.Command, args []string) error { + err := filters.Merge3{ + OriginalPath: r.ancestor, + UpdatedPath: r.fromDir, + DestPath: r.toDir, + }.Merge() + if err != nil { + return err + } + return nil +} diff --git a/cmd/config/internal/commands/merge3_test.go b/cmd/config/internal/commands/merge3_test.go new file mode 100644 index 000000000..7b28d1513 --- /dev/null +++ b/cmd/config/internal/commands/merge3_test.go @@ -0,0 +1,236 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package commands + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/cmd/config/internal/commands" + "sigs.k8s.io/kustomize/kyaml/copyutil" +) + +// TestMerge3Command verifies the merge3 correctly applies the diff between 2 sets of resources into another +func TestMerge3Command(t *testing.T) { + datadir, err := ioutil.TempDir("", "test-data") + defer os.RemoveAll(datadir) + if !assert.NoError(t, err) { + return + } + + err = ioutil.WriteFile(filepath.Join(datadir, "java-deployment.resource.yaml"), []byte(`apiVersion: apps/v1 +kind: Deployment +metadata: + name: app + labels: + app: java +spec: + replicas: 1 + selector: + matchLabels: + app: java + template: + metadata: + labels: + app: java + spec: + restartPolicy: Always + containers: + - name: app + image: gcr.io/project/app:version + command: + - java + - -jar + - /app.jar + ports: + - containerPort: 8080 + envFrom: + - configMapRef: + name: app-config + env: + - name: JAVA_OPTS + value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap + -Djava.security.egd=file:/dev/./urandom + imagePullPolicy: Always + minReadySeconds: 5 +`), 0600) + if !assert.NoError(t, err) { + return + } + + expected_dir, err := ioutil.TempDir("", "test-data-expected") + defer os.RemoveAll(expected_dir) + if !assert.NoError(t, err) { + return + } + + err = ioutil.WriteFile(filepath.Join(expected_dir, "java-deployment.resource.yaml"), []byte(`apiVersion: apps/v1 +kind: Deployment +metadata: + name: app + labels: + app: java + new-local: label + new-remote: label +spec: + replicas: 3 + selector: + matchLabels: + app: java + template: + metadata: + labels: + app: java + spec: + restartPolicy: Always + containers: + - name: app + image: gcr.io/project/app:version + command: + - java + - -jar + - /app.jar + - otherstuff + args: + - foo + ports: + - containerPort: 8080 + envFrom: + - configMapRef: + name: app-config + env: + - name: JAVA_OPTS + value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap + -Djava.security.egd=file:/dev/./urandom + imagePullPolicy: Always + minReadySeconds: 20 +`), 0600) + if !assert.NoError(t, err) { + return + } + + updated_dir, err := ioutil.TempDir("", "test-data-updated") + defer os.RemoveAll(updated_dir) + if !assert.NoError(t, err) { + return + } + + err = ioutil.WriteFile(filepath.Join(updated_dir, "java-deployment.resource.yaml"), []byte(`apiVersion: apps/v1 +kind: Deployment +metadata: + name: app + labels: + app: java + new-remote: label +spec: + replicas: 3 + selector: + matchLabels: + app: java + template: + metadata: + labels: + app: java + spec: + restartPolicy: Always + containers: + - name: app + image: gcr.io/project/app:version + command: + - java + - -jar + - /app.jar + - otherstuff + ports: + - containerPort: 8080 + envFrom: + - configMapRef: + name: app-config + env: + - name: JAVA_OPTS + value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap + -Djava.security.egd=file:/dev/./urandom + imagePullPolicy: Always + minReadySeconds: 5 +`), 0600) + if !assert.NoError(t, err) { + return + } + + dest_dir, err := ioutil.TempDir("", "test-data-dest") + defer os.RemoveAll(dest_dir) + if !assert.NoError(t, err) { + return + } + + err = ioutil.WriteFile(filepath.Join(dest_dir, "java-deployment.resource.yaml"), []byte(`apiVersion: apps/v1 +kind: Deployment +metadata: + name: app + labels: + app: java + new-local: label +spec: + replicas: 2 + selector: + matchLabels: + app: java + template: + metadata: + labels: + app: java + spec: + restartPolicy: Always + containers: + - name: app + image: gcr.io/project/app:version + command: + - java + - -jar + - /app.jar + args: + - foo + ports: + - containerPort: 8080 + envFrom: + - configMapRef: + name: app-config + env: + - name: JAVA_OPTS + value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap + -Djava.security.egd=file:/dev/./urandom + imagePullPolicy: Always + minReadySeconds: 20 +`), 0600) + if !assert.NoError(t, err) { + return + } + + // Perform merge3 with newly created sets + r := commands.GetMerge3Runner("") + r.Command.SetArgs([]string{ + "--ancestor", + datadir, + "--from", + updated_dir, + "--to", + dest_dir, + }) + if !assert.NoError(t, r.Command.Execute()) { + return + } + + diffs, err := copyutil.Diff(dest_dir, expected_dir) + if !assert.NoError(t, err) { + t.FailNow() + } + + // Verify there are no diffs + if !assert.Empty(t, diffs.List()) { + t.FailNow() + } +} \ No newline at end of file