From c5b66dae6ccb4814338982bd4eecf35d88163c57 Mon Sep 17 00:00:00 2001 From: Nick Kubala Date: Tue, 22 Sep 2020 10:12:04 -0700 Subject: [PATCH] Move deployers into separate packages (#4812) * Move deployers into separate packages * review comments and fixing linters * rename a few functions --- cmd/skaffold/app/cmd/debug.go | 4 +- cmd/skaffold/app/cmd/filter.go | 7 +- integration/render_test.go | 9 +- pkg/skaffold/debug/debug.go | 9 +- pkg/skaffold/debug/debug_test.go | 4 +- pkg/skaffold/deploy/deploy_mux.go | 43 +--- pkg/skaffold/deploy/{ => helm}/helm.go | 48 ++-- pkg/skaffold/deploy/{ => helm}/helm_test.go | 44 ++-- pkg/skaffold/deploy/helm/parse.go | 89 +++++++ .../{util_test.go => helm/parse_test.go} | 17 +- pkg/skaffold/deploy/{ => kpt}/kpt.go | 82 +++---- pkg/skaffold/deploy/{ => kpt}/kpt_test.go | 37 +-- pkg/skaffold/deploy/kubectl/cli.go | 23 +- pkg/skaffold/deploy/kubectl/constants.go | 73 ++++++ pkg/skaffold/deploy/{ => kubectl}/kubectl.go | 74 +++--- .../deploy/{ => kubectl}/kubectl_test.go | 229 +++++++----------- .../deploy/{ => kustomize}/kustomize.go | 172 +++---------- .../deploy/{ => kustomize}/kustomize_test.go | 71 +++--- pkg/skaffold/deploy/kustomize/util.go | 145 +++++++++++ pkg/skaffold/deploy/{ => label}/labeller.go | 6 +- pkg/skaffold/deploy/{ => label}/labels.go | 15 +- .../deploy/{ => label}/labels_test.go | 7 +- .../deploy/{ => status}/status_check.go | 21 +- .../deploy/{ => status}/status_check_test.go | 34 +-- pkg/skaffold/deploy/transformations.go | 37 --- pkg/skaffold/deploy/types/types.go | 40 +++ pkg/skaffold/deploy/util.go | 172 ------------- pkg/skaffold/deploy/{ => util}/logfile.go | 2 +- .../deploy/{ => util}/logfile_test.go | 2 +- pkg/skaffold/deploy/util/stringset.go | 46 ++++ pkg/skaffold/deploy/util/util.go | 39 +++ pkg/skaffold/initializer/analyze/helm.go | 2 +- pkg/skaffold/initializer/analyze/kustomize.go | 2 +- pkg/skaffold/initializer/deploy/kustomize.go | 4 +- pkg/skaffold/kubernetes/manifest/gcs.go | 44 ++++ .../kubectl => kubernetes/manifest}/images.go | 4 +- .../manifest}/images_test.go | 2 +- .../kubectl => kubernetes/manifest}/labels.go | 2 +- .../manifest}/labels_test.go | 2 +- .../manifest}/manifests.go | 3 +- .../manifest}/manifests_test.go | 2 +- .../manifest}/namespace_test.go | 2 +- .../manifest}/namespaces.go | 2 +- .../kubernetes/manifest/transformations.go | 55 +++++ pkg/skaffold/kubernetes/manifest/util.go | 73 ++++++ .../manifest}/visitor.go | 2 +- .../manifest}/visitor_test.go | 2 +- .../portforward/resource_forwarder_test.go | 12 +- pkg/skaffold/runner/build_deploy.go | 4 +- pkg/skaffold/runner/deploy.go | 4 +- pkg/skaffold/runner/deploy_test.go | 7 +- pkg/skaffold/runner/new.go | 21 +- pkg/skaffold/runner/new_test.go | 16 +- pkg/skaffold/runner/runner.go | 6 +- pkg/skaffold/runner/runner_test.go | 15 +- 55 files changed, 1052 insertions(+), 837 deletions(-) rename pkg/skaffold/deploy/{ => helm}/helm.go (92%) rename pkg/skaffold/deploy/{ => helm}/helm_test.go (98%) create mode 100644 pkg/skaffold/deploy/helm/parse.go rename pkg/skaffold/deploy/{util_test.go => helm/parse_test.go} (88%) rename pkg/skaffold/deploy/{ => kpt}/kpt.go (83%) rename pkg/skaffold/deploy/{ => kpt}/kpt_test.go (96%) create mode 100644 pkg/skaffold/deploy/kubectl/constants.go rename pkg/skaffold/deploy/{ => kubectl}/kubectl.go (78%) rename pkg/skaffold/deploy/{ => kubectl}/kubectl_test.go (78%) rename pkg/skaffold/deploy/{ => kustomize}/kustomize.go (61%) rename pkg/skaffold/deploy/{ => kustomize}/kustomize_test.go (89%) create mode 100644 pkg/skaffold/deploy/kustomize/util.go rename pkg/skaffold/deploy/{ => label}/labeller.go (95%) rename pkg/skaffold/deploy/{ => label}/labels.go (93%) rename pkg/skaffold/deploy/{ => label}/labels_test.go (94%) rename pkg/skaffold/deploy/{ => status}/status_check.go (93%) rename pkg/skaffold/deploy/{ => status}/status_check_test.go (95%) delete mode 100644 pkg/skaffold/deploy/transformations.go create mode 100644 pkg/skaffold/deploy/types/types.go delete mode 100644 pkg/skaffold/deploy/util.go rename pkg/skaffold/deploy/{ => util}/logfile.go (99%) rename pkg/skaffold/deploy/{ => util}/logfile_test.go (99%) create mode 100644 pkg/skaffold/deploy/util/stringset.go create mode 100644 pkg/skaffold/deploy/util/util.go create mode 100644 pkg/skaffold/kubernetes/manifest/gcs.go rename pkg/skaffold/{deploy/kubectl => kubernetes/manifest}/images.go (97%) rename pkg/skaffold/{deploy/kubectl => kubernetes/manifest}/images_test.go (99%) rename pkg/skaffold/{deploy/kubectl => kubernetes/manifest}/labels.go (99%) rename pkg/skaffold/{deploy/kubectl => kubernetes/manifest}/labels_test.go (99%) rename pkg/skaffold/{deploy/kubectl => kubernetes/manifest}/manifests.go (98%) rename pkg/skaffold/{deploy/kubectl => kubernetes/manifest}/manifests_test.go (99%) rename pkg/skaffold/{deploy/kubectl => kubernetes/manifest}/namespace_test.go (99%) rename pkg/skaffold/{deploy/kubectl => kubernetes/manifest}/namespaces.go (98%) create mode 100644 pkg/skaffold/kubernetes/manifest/transformations.go create mode 100644 pkg/skaffold/kubernetes/manifest/util.go rename pkg/skaffold/{deploy/kubectl => kubernetes/manifest}/visitor.go (99%) rename pkg/skaffold/{deploy/kubectl => kubernetes/manifest}/visitor_test.go (99%) diff --git a/cmd/skaffold/app/cmd/debug.go b/cmd/skaffold/app/cmd/debug.go index 0202043c7be..35ed11fded1 100644 --- a/cmd/skaffold/app/cmd/debug.go +++ b/cmd/skaffold/app/cmd/debug.go @@ -23,7 +23,7 @@ import ( "github.com/spf13/cobra" debugging "github.com/GoogleContainerTools/skaffold/pkg/skaffold/debug" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest" ) // for tests @@ -44,7 +44,7 @@ func NewCmdDebug() *cobra.Command { func runDebug(ctx context.Context, out io.Writer) error { opts.PortForward.ForwardPods = true - deploy.AddManifestTransform(debugging.ApplyDebuggingTransforms) + manifest.AddTransform(debugging.ApplyDebuggingTransforms) return doDev(ctx, out) } diff --git a/cmd/skaffold/app/cmd/filter.go b/cmd/skaffold/app/cmd/filter.go index 02aeb4f6373..db60e725b92 100644 --- a/cmd/skaffold/app/cmd/filter.go +++ b/cmd/skaffold/app/cmd/filter.go @@ -29,8 +29,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" debugging "github.com/GoogleContainerTools/skaffold/pkg/skaffold/debug" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" ) @@ -65,7 +64,7 @@ func runFilter(ctx context.Context, out io.Writer, debuggingFilters bool, buildA if err != nil { return err } - manifestList := kubectl.ManifestList([][]byte{bytes}) + manifestList := manifest.ManifestList([][]byte{bytes}) if debuggingFilters { // TODO(bdealwis): refactor this code debugHelpersRegistry, err := config.GetDebugHelpersRegistry(opts.GlobalConfig) @@ -77,7 +76,7 @@ func runFilter(ctx context.Context, out io.Writer, debuggingFilters bool, buildA return err } - manifestList, err = debugging.ApplyDebuggingTransforms(manifestList, buildArtifacts, deploy.Registries{ + manifestList, err = debugging.ApplyDebuggingTransforms(manifestList, buildArtifacts, manifest.Registries{ DebugHelpersRegistry: debugHelpersRegistry, InsecureRegistries: insecureRegistries, }) diff --git a/integration/render_test.go b/integration/render_test.go index a330351dc66..afbd0904e5c 100644 --- a/integration/render_test.go +++ b/integration/render_test.go @@ -31,7 +31,8 @@ import ( "github.com/GoogleContainerTools/skaffold/integration/skaffold" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/helm" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/testutil" @@ -76,7 +77,7 @@ spec: t.NewTempDir(). Write("deployment.yaml", test.input). Chdir() - deployer, err := deploy.NewKubectlDeployer(&runcontext.RunContext{ + deployer, err := kubectl.NewDeployer(&runcontext.RunContext{ WorkingDir: ".", Cfg: latest.Pipeline{ Deploy: latest.DeployConfig{ @@ -231,7 +232,7 @@ spec: Write("deployment.yaml", test.input). Chdir() - deployer, err := deploy.NewKubectlDeployer(&runcontext.RunContext{ + deployer, err := kubectl.NewDeployer(&runcontext.RunContext{ WorkingDir: ".", Cfg: latest.Pipeline{ Deploy: latest.DeployConfig{ @@ -419,7 +420,7 @@ spec: } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - deployer := deploy.NewHelmDeployer(&runcontext.RunContext{ + deployer := helm.NewDeployer(&runcontext.RunContext{ Cfg: latest.Pipeline{ Deploy: latest.DeployConfig{ DeployType: latest.DeployType{ diff --git a/pkg/skaffold/debug/debug.go b/pkg/skaffold/debug/debug.go index 38a8a7c7ce1..d530eaae007 100644 --- a/pkg/skaffold/debug/debug.go +++ b/pkg/skaffold/debug/debug.go @@ -29,9 +29,8 @@ import ( "k8s.io/client-go/kubernetes/scheme" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" ) @@ -50,7 +49,7 @@ var ( ) // ApplyDebuggingTransforms applies language-platform-specific transforms to a list of manifests. -func ApplyDebuggingTransforms(l kubectl.ManifestList, builds []build.Artifact, registries deploy.Registries) (kubectl.ManifestList, error) { +func ApplyDebuggingTransforms(l manifest.ManifestList, builds []build.Artifact, registries manifest.Registries) (manifest.ManifestList, error) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -63,8 +62,8 @@ func ApplyDebuggingTransforms(l kubectl.ManifestList, builds []build.Artifact, r return applyDebuggingTransforms(l, retriever, registries.DebugHelpersRegistry) } -func applyDebuggingTransforms(l kubectl.ManifestList, retriever configurationRetriever, debugHelpersRegistry string) (kubectl.ManifestList, error) { - var updated kubectl.ManifestList +func applyDebuggingTransforms(l manifest.ManifestList, retriever configurationRetriever, debugHelpersRegistry string) (manifest.ManifestList, error) { + var updated manifest.ManifestList for _, manifest := range l { obj, _, err := decodeFromYaml(manifest, nil, nil) if err != nil { diff --git a/pkg/skaffold/debug/debug_test.go b/pkg/skaffold/debug/debug_test.go index 8d275d2ccae..62dfc606e78 100644 --- a/pkg/skaffold/debug/debug_test.go +++ b/pkg/skaffold/debug/debug_test.go @@ -24,7 +24,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -509,7 +509,7 @@ spec: return imageConfiguration{}, nil } - result, err := applyDebuggingTransforms(kubectl.ManifestList{[]byte(test.in)}, retriever, "HELPERS") + result, err := applyDebuggingTransforms(manifest.ManifestList{[]byte(test.in)}, retriever, "HELPERS") t.CheckErrorAndDeepEqual(test.shouldErr, err, test.out, result.String()) }) diff --git a/pkg/skaffold/deploy/deploy_mux.go b/pkg/skaffold/deploy/deploy_mux.go index 6b74b94616e..33568b3da7b 100644 --- a/pkg/skaffold/deploy/deploy_mux.go +++ b/pkg/skaffold/deploy/deploy_mux.go @@ -20,10 +20,11 @@ import ( "bytes" "context" "io" - "sort" "strings" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/util" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest" ) // DeployerMux forwards all method calls to the deployers it contains. @@ -31,56 +32,30 @@ import ( // it collects the results and returns it in bulk. type DeployerMux []Deployer -type unit struct{} - -// stringSet helps to de-duplicate a set of strings. -type stringSet map[string]unit - -func newStringSet() stringSet { - return make(map[string]unit) -} - -// insert adds strings to the set. -func (s stringSet) insert(strings ...string) { - for _, item := range strings { - s[item] = unit{} - } -} - -// toList returns the sorted list of inserted strings. -func (s stringSet) toList() []string { - var res []string - for item := range s { - res = append(res, item) - } - sort.Strings(res) - return res -} - func (m DeployerMux) Deploy(ctx context.Context, w io.Writer, as []build.Artifact) ([]string, error) { - seenNamespaces := newStringSet() + seenNamespaces := util.NewStringSet() for _, deployer := range m { namespaces, err := deployer.Deploy(ctx, w, as) if err != nil { return nil, err } - seenNamespaces.insert(namespaces...) + seenNamespaces.Insert(namespaces...) } - return seenNamespaces.toList(), nil + return seenNamespaces.ToList(), nil } func (m DeployerMux) Dependencies() ([]string, error) { - deps := newStringSet() + deps := util.NewStringSet() for _, deployer := range m { result, err := deployer.Dependencies() if err != nil { return nil, err } - deps.insert(result...) + deps.Insert(result...) } - return deps.toList(), nil + return deps.ToList(), nil } func (m DeployerMux) Cleanup(ctx context.Context, w io.Writer) error { @@ -103,5 +78,5 @@ func (m DeployerMux) Render(ctx context.Context, w io.Writer, as []build.Artifac } allResources := strings.Join(resources, "\n---\n") - return outputRenderedManifests(allResources, filepath, w) + return manifest.Write(allResources, filepath, w) } diff --git a/pkg/skaffold/deploy/helm.go b/pkg/skaffold/deploy/helm/helm.go similarity index 92% rename from pkg/skaffold/deploy/helm.go rename to pkg/skaffold/deploy/helm/helm.go index 62461190eee..632544d4f91 100644 --- a/pkg/skaffold/deploy/helm.go +++ b/pkg/skaffold/deploy/helm/helm.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deploy +package helm import ( "bufio" @@ -35,7 +35,7 @@ import ( "time" "github.com/blang/semver" - "github.com/cenkalti/backoff/v4" + backoff "github.com/cenkalti/backoff/v4" "github.com/mitchellh/go-homedir" "github.com/sirupsen/logrus" @@ -43,7 +43,11 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/label" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/types" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/walk" @@ -69,8 +73,8 @@ var ( osExecutable = os.Executable ) -// HelmDeployer deploys workflows using the helm CLI -type HelmDeployer struct { +// Deployer deploys workflows using the helm CLI +type Deployer struct { *latest.HelmDeploy kubeContext string @@ -89,9 +93,9 @@ type HelmDeployer struct { bV semver.Version } -// NewHelmDeployer returns a configured HelmDeployer -func NewHelmDeployer(cfg Config, labels map[string]string) *HelmDeployer { - return &HelmDeployer{ +// NewDeployer returns a configured Deployer +func NewDeployer(cfg kubectl.Config, labels map[string]string) *Deployer { + return &Deployer{ HelmDeploy: cfg.Pipeline().Deploy.HelmDeploy, kubeContext: cfg.GetKubeContext(), kubeConfig: cfg.GetKubeConfig(), @@ -103,7 +107,7 @@ func NewHelmDeployer(cfg Config, labels map[string]string) *HelmDeployer { } // Deploy deploys the build results to the Kubernetes cluster -func (h *HelmDeployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact) ([]string, error) { +func (h *Deployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact) ([]string, error) { hv, err := h.binVer(ctx) if err != nil { return nil, fmt.Errorf(versionErrorString, err) @@ -111,7 +115,7 @@ func (h *HelmDeployer) Deploy(ctx context.Context, out io.Writer, builds []build logrus.Infof("Deploying with helm v%s ...", hv) - var dRes []Artifact + var dRes []types.Artifact nsMap := map[string]struct{}{} valuesSet := map[string]bool{} @@ -149,7 +153,7 @@ func (h *HelmDeployer) Deploy(ctx context.Context, out io.Writer, builds []build } } - if err := labelDeployResults(h.labels, dRes); err != nil { + if err := label.Apply(h.labels, dRes); err != nil { return nil, fmt.Errorf("adding labels: %w", err) } @@ -163,7 +167,7 @@ func (h *HelmDeployer) Deploy(ctx context.Context, out io.Writer, builds []build } // Dependencies returns a list of files that the deployer depends on. -func (h *HelmDeployer) Dependencies() ([]string, error) { +func (h *Deployer) Dependencies() ([]string, error) { var deps []string for _, release := range h.Releases { @@ -223,7 +227,7 @@ func (h *HelmDeployer) Dependencies() ([]string, error) { } // Cleanup deletes what was deployed by calling Deploy. -func (h *HelmDeployer) Cleanup(ctx context.Context, out io.Writer) error { +func (h *Deployer) Cleanup(ctx context.Context, out io.Writer) error { hv, err := h.binVer(ctx) if err != nil { return fmt.Errorf(versionErrorString, err) @@ -259,7 +263,7 @@ func (h *HelmDeployer) Cleanup(ctx context.Context, out io.Writer) error { } // Render generates the Kubernetes manifests and writes them out -func (h *HelmDeployer) Render(ctx context.Context, out io.Writer, builds []build.Artifact, offline bool, filepath string) error { +func (h *Deployer) Render(ctx context.Context, out io.Writer, builds []build.Artifact, offline bool, filepath string) error { hv, err := h.binVer(ctx) if err != nil { return fmt.Errorf(versionErrorString, err) @@ -321,11 +325,11 @@ func (h *HelmDeployer) Render(ctx context.Context, out io.Writer, builds []build renderedManifests.Write(outBuffer.Bytes()) } - return outputRenderedManifests(renderedManifests.String(), filepath, out) + return manifest.Write(renderedManifests.String(), filepath, out) } // exec executes the helm command, writing combined stdout/stderr to the provided writer -func (h *HelmDeployer) exec(ctx context.Context, out io.Writer, useSecrets bool, env []string, args ...string) error { +func (h *Deployer) exec(ctx context.Context, out io.Writer, useSecrets bool, env []string, args ...string) error { if args[0] != "version" { args = append([]string{"--kube-context", h.kubeContext}, args...) args = append(args, h.Flags.Global...) @@ -350,7 +354,7 @@ func (h *HelmDeployer) exec(ctx context.Context, out io.Writer, useSecrets bool, } // deployRelease deploys a single release -func (h *HelmDeployer) deployRelease(ctx context.Context, out io.Writer, r latest.HelmRelease, builds []build.Artifact, valuesSet map[string]bool, helmVersion semver.Version) ([]Artifact, error) { +func (h *Deployer) deployRelease(ctx context.Context, out io.Writer, r latest.HelmRelease, builds []build.Artifact, valuesSet map[string]bool, helmVersion semver.Version) ([]types.Artifact, error) { releaseName, err := util.ExpandEnvTemplate(r.Name, nil) if err != nil { return nil, fmt.Errorf("cannot parse the release name template: %w", err) @@ -413,10 +417,10 @@ func (h *HelmDeployer) deployRelease(ctx context.Context, out io.Writer, r lates } else { if r.UpgradeOnChange != nil && !*r.UpgradeOnChange { logrus.Infof("Release %s already installed...", releaseName) - return []Artifact{}, nil + return []types.Artifact{}, nil } else if r.UpgradeOnChange == nil && r.Remote { logrus.Infof("Release %s not upgraded as it is remote...", releaseName) - return []Artifact{}, nil + return []types.Artifact{}, nil } } @@ -474,7 +478,7 @@ func (h *HelmDeployer) deployRelease(ctx context.Context, out io.Writer, r lates } // getRelease confirms that a release is visible to helm -func (h *HelmDeployer) getRelease(ctx context.Context, helmVersion semver.Version, releaseName string, namespace string) (bytes.Buffer, error) { +func (h *Deployer) getRelease(ctx context.Context, helmVersion semver.Version, releaseName string, namespace string) (bytes.Buffer, error) { // Retry, because under Helm 2, at least, a release may not be immediately visible opts := backoff.NewExponentialBackOff() opts.MaxElapsedTime = 4 * time.Second @@ -495,7 +499,7 @@ func (h *HelmDeployer) getRelease(ctx context.Context, helmVersion semver.Versio } // binVer returns the version of the helm binary found in PATH. May be cached. -func (h *HelmDeployer) binVer(ctx context.Context) (semver.Version, error) { +func (h *Deployer) binVer(ctx context.Context) (semver.Version, error) { // Return the cached version value if non-zero if h.bV.Major != 0 || h.bV.Minor != 0 { return h.bV, nil @@ -732,7 +736,7 @@ func envVarForImage(imageName string, digest string) map[string]string { } // packageChart packages the chart and returns the path to the resulting chart archive -func (h *HelmDeployer) packageChart(ctx context.Context, r latest.HelmRelease) (string, error) { +func (h *Deployer) packageChart(ctx context.Context, r latest.HelmRelease) (string, error) { // Allow a test to sneak a predictable path in tmpDir := h.pkgTmpDir @@ -778,7 +782,7 @@ func (h *HelmDeployer) packageChart(ctx context.Context, r latest.HelmRelease) ( return output[idx:], nil } -func (h *HelmDeployer) generateSkaffoldDebugFilter(buildsFile string) []string { +func (h *Deployer) generateSkaffoldDebugFilter(buildsFile string) []string { args := []string{"filter", "--debugging", "--kube-context", h.kubeContext} if len(buildsFile) > 0 { args = append(args, "--build-artifacts", buildsFile) diff --git a/pkg/skaffold/deploy/helm_test.go b/pkg/skaffold/deploy/helm/helm_test.go similarity index 98% rename from pkg/skaffold/deploy/helm_test.go rename to pkg/skaffold/deploy/helm/helm_test.go index 8a3573cd982..9c43346aec7 100644 --- a/pkg/skaffold/deploy/helm_test.go +++ b/pkg/skaffold/deploy/helm/helm_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deploy +package helm import ( "context" @@ -26,6 +26,7 @@ import ( "github.com/mitchellh/go-homedir" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" schemautil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util" @@ -307,9 +308,6 @@ var testDeployCreateNamespaceConfig = latest.HelmDeploy{ }}, } -var testNamespace = "testNamespace" -var testNamespace2 = "testNamespace2" - var validDeployYaml = ` # Source: skaffold-helm/templates/deployment.yaml apiVersion: apps/v1 @@ -422,7 +420,7 @@ func TestBinVer(t *testing.T) { testutil.Run(t, test.description, func(t *testutil.T) { t.Override(&util.DefaultExecCommand, testutil.CmdRunWithOutput("helm version --client", test.helmVersion)) - deployer := NewHelmDeployer(&helmConfig{ + deployer := NewDeployer(&helmConfig{ helm: testDeployConfig, }, nil) ver, err := deployer.binVer(context.TODO()) @@ -447,7 +445,7 @@ func TestHelmDeploy(t *testing.T) { commands util.Command helm latest.HelmDeploy namespace string - configure func(*HelmDeployer) + configure func(*Deployer) builds []build.Artifact force bool shouldErr bool @@ -485,7 +483,7 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test --namespace testNamespace -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get all --namespace testNamespace skaffold-helm --kubeconfig kubeconfig"), helm: testDeployConfig, - namespace: testNamespace, + namespace: kubectl.TestNamespace, builds: testBuilds, }, { @@ -530,7 +528,7 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test --namespace testNamespace -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get all --namespace testNamespace skaffold-helm --kubeconfig kubeconfig"), helm: testDeployConfig, - namespace: testNamespace, + namespace: kubectl.TestNamespace, builds: testBuilds, }, { @@ -542,7 +540,7 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test --namespace testNamespace -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get all --namespace testNamespace skaffold-helm --kubeconfig kubeconfig"), helm: testDeployNamespacedConfig, - namespace: testNamespace, + namespace: kubectl.TestNamespace, builds: testBuilds, }, { @@ -587,7 +585,7 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test --namespace testNamespace -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get all --namespace testNamespace skaffold-helm --kubeconfig kubeconfig"), helm: testDeployConfig, - namespace: testNamespace, + namespace: kubectl.TestNamespace, builds: testBuilds, }, { @@ -599,7 +597,7 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext upgrade skaffold-helm examples/test --namespace testNamespace -f skaffold-overrides.yaml --set-string image=docker.io:5000/skaffold-helm:3605e7bc17cf46e53f4d81c4cbc24e5b4c495184 --set some.key=somevalue --kubeconfig kubeconfig"). AndRun("helm --kube-context kubecontext get all --namespace testNamespace skaffold-helm --kubeconfig kubeconfig"), helm: testDeployNamespacedConfig, - namespace: testNamespace, + namespace: kubectl.TestNamespace, builds: testBuilds, }, { @@ -891,7 +889,7 @@ func TestHelmDeploy(t *testing.T) { shouldErr: true, helm: testDeployConfig, builds: testBuilds, - configure: func(deployer *HelmDeployer) { deployer.enableDebug = true }, + configure: func(deployer *Deployer) { deployer.enableDebug = true }, }, { description: "debug for helm3.1 success", @@ -903,7 +901,7 @@ func TestHelmDeploy(t *testing.T) { AndRun("helm --kube-context kubecontext get all skaffold-helm --kubeconfig kubeconfig"), helm: testDeployConfig, builds: testBuilds, - configure: func(deployer *HelmDeployer) { deployer.enableDebug = true }, + configure: func(deployer *Deployer) { deployer.enableDebug = true }, }, { description: "helm3.1 should fail to deploy with createNamespace option", @@ -946,7 +944,7 @@ func TestHelmDeploy(t *testing.T) { t.Override(&util.DefaultExecCommand, test.commands) t.Override(&osExecutable, func() (string, error) { return "SKAFFOLD-BINARY", nil }) - deployer := NewHelmDeployer(&helmConfig{ + deployer := NewDeployer(&helmConfig{ helm: test.helm, namespace: test.namespace, force: test.force, @@ -1012,7 +1010,7 @@ func TestHelmCleanup(t *testing.T) { CmdRunWithOutput("helm version --client", version31). AndRun("helm --kube-context kubecontext delete skaffold-helm --namespace testNamespace --kubeconfig kubeconfig"), helm: testDeployConfig, - namespace: testNamespace, + namespace: kubectl.TestNamespace, builds: testBuilds, }, { @@ -1021,7 +1019,7 @@ func TestHelmCleanup(t *testing.T) { CmdRunWithOutput("helm version --client", version31). AndRun("helm --kube-context kubecontext delete skaffold-helm --namespace testNamespace --kubeconfig kubeconfig"), helm: testDeployNamespacedConfig, - namespace: testNamespace, + namespace: kubectl.TestNamespace, builds: testBuilds, }, } @@ -1032,7 +1030,7 @@ func TestHelmCleanup(t *testing.T) { t.Override(&util.OSEnviron, func() []string { return []string{"FOO=FOOBAR"} }) t.Override(&util.DefaultExecCommand, test.commands) - deployer := NewHelmDeployer(&helmConfig{ + deployer := NewDeployer(&helmConfig{ helm: test.helm, namespace: test.namespace, }, nil) @@ -1066,7 +1064,7 @@ func TestParseHelmRelease(t *testing.T) { } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - _, err := parseRuntimeObject(testNamespace, test.yaml) + _, err := parseRuntimeObject(kubectl.TestNamespace, test.yaml) t.CheckError(test.shouldErr, err) }) @@ -1134,7 +1132,7 @@ func TestHelmDependencies(t *testing.T) { tmpDir := t.NewTempDir(). Touch(test.files...) - deployer := NewHelmDeployer(&helmConfig{ + deployer := NewDeployer(&helmConfig{ helm: latest.HelmDeploy{ Releases: []latest.HelmRelease{{ Name: "skaffold-helm", @@ -1333,7 +1331,7 @@ func TestHelmRender(t *testing.T) { t.Override(&util.OSEnviron, func() []string { return []string{"FOO=FOOBAR"} }) - deployer := NewHelmDeployer(&helmConfig{ + deployer := NewDeployer(&helmConfig{ helm: test.helm, }, nil) @@ -1404,7 +1402,7 @@ func TestGenerateSkaffoldDebugFilter(t *testing.T) { } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - h := NewHelmDeployer(&helmConfig{ + h := NewDeployer(&helmConfig{ helm: testDeployConfig, }, nil) result := h.generateSkaffoldDebugFilter(test.buildFile) @@ -1421,8 +1419,8 @@ type helmConfig struct { } func (c *helmConfig) ForceDeploy() bool { return c.force } -func (c *helmConfig) GetKubeConfig() string { return testKubeConfig } -func (c *helmConfig) GetKubeContext() string { return testKubeContext } +func (c *helmConfig) GetKubeConfig() string { return kubectl.TestKubeConfig } +func (c *helmConfig) GetKubeContext() string { return kubectl.TestKubeContext } func (c *helmConfig) GetKubeNamespace() string { return c.namespace } func (c *helmConfig) Pipeline() latest.Pipeline { var pipeline latest.Pipeline diff --git a/pkg/skaffold/deploy/helm/parse.go b/pkg/skaffold/deploy/helm/parse.go new file mode 100644 index 00000000000..edd7b5145a0 --- /dev/null +++ b/pkg/skaffold/deploy/helm/parse.go @@ -0,0 +1,89 @@ +/* +Copyright 2020 The Skaffold Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package helm + +import ( + "bufio" + "bytes" + "fmt" + "io" + + "github.com/sirupsen/logrus" + k8syaml "k8s.io/apimachinery/pkg/util/yaml" + "k8s.io/client-go/kubernetes/scheme" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/types" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest" +) + +func parseReleaseInfo(namespace string, b *bufio.Reader) []types.Artifact { + var results []types.Artifact + + r := k8syaml.NewYAMLReader(b) + for i := 0; ; i++ { + doc, err := r.Read() + if err == io.EOF { + break + } + if err != nil { + logrus.Infof("error parsing object from string: %s", err.Error()) + continue + } + objNamespace, err := getObjectNamespaceIfDefined(doc, namespace) + if err != nil { + logrus.Infof("error parsing object from string: %s", err.Error()) + continue + } + obj, err := parseRuntimeObject(objNamespace, doc) + if err != nil { + if i > 0 { + logrus.Infof(err.Error()) + } + } else { + results = append(results, *obj) + logrus.Debugf("found deployed object: %+v", obj.Obj) + } + } + + return results +} + +func parseRuntimeObject(namespace string, b []byte) (*types.Artifact, error) { + d := scheme.Codecs.UniversalDeserializer() + obj, _, err := d.Decode(b, nil, nil) + if err != nil { + return nil, fmt.Errorf("error decoding parsed yaml: %s", err.Error()) + } + return &types.Artifact{ + Obj: obj, + Namespace: namespace, + }, nil +} + +func getObjectNamespaceIfDefined(doc []byte, ns string) (string, error) { + if i := bytes.Index(doc, []byte("apiVersion")); i >= 0 { + manifests := manifest.ManifestList{doc[i:]} + namespaces, err := manifests.CollectNamespaces() + if err != nil { + return ns, err + } + if len(namespaces) > 0 { + return namespaces[0], nil + } + } + return ns, nil +} diff --git a/pkg/skaffold/deploy/util_test.go b/pkg/skaffold/deploy/helm/parse_test.go similarity index 88% rename from pkg/skaffold/deploy/util_test.go rename to pkg/skaffold/deploy/helm/parse_test.go index bb7826a34c0..d1e6b5111b0 100644 --- a/pkg/skaffold/deploy/util_test.go +++ b/pkg/skaffold/deploy/helm/parse_test.go @@ -1,5 +1,5 @@ /* -Copyright 2019 The Skaffold Authors +Copyright 2020 The Skaffold Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deploy +package helm import ( "bufio" @@ -23,6 +23,7 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/types" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -30,7 +31,7 @@ func TestParseReleaseInfo(t *testing.T) { tests := []struct { description string yaml []byte - expected []Artifact + expected []types.Artifact }{ { description: "parse valid release info yaml with single artifact with namespace", @@ -55,7 +56,7 @@ spec: selector: app: skaffold-helm release: skaffold-helm`), - expected: []Artifact{{Namespace: "test"}}, + expected: []types.Artifact{{Namespace: "test"}}, }, { description: "parse valid release info yaml with single artifact without namespace sets helm namespace", @@ -79,7 +80,7 @@ spec: selector: app: skaffold-helm release: skaffold-helm`), - expected: []Artifact{{ + expected: []types.Artifact{{ Namespace: "testNamespace", }}, }, @@ -126,7 +127,7 @@ spec: backend: serviceName: skaffold-helm-skaffold-helm servicePort: 80`), - expected: []Artifact{{Namespace: "testNamespace"}, {Namespace: "test"}}, + expected: []types.Artifact{{Namespace: "testNamespace"}, {Namespace: "test"}}, }, { description: "parse invalid release info yaml", @@ -138,9 +139,9 @@ spec: testutil.Run(t, test.description, func(t *testutil.T) { r := bufio.NewReader(bytes.NewBuffer(test.yaml)) - actual := parseReleaseInfo(testNamespace, r) + actual := parseReleaseInfo("testNamespace", r) - t.CheckDeepEqual(test.expected, actual, cmpopts.IgnoreFields(Artifact{}, "Obj")) + t.CheckDeepEqual(test.expected, actual, cmpopts.IgnoreFields(types.Artifact{}, "Obj")) }) } } diff --git a/pkg/skaffold/deploy/kpt.go b/pkg/skaffold/deploy/kpt/kpt.go similarity index 83% rename from pkg/skaffold/deploy/kpt.go rename to pkg/skaffold/deploy/kpt/kpt.go index 336767312dd..cc1b96a7138 100644 --- a/pkg/skaffold/deploy/kpt.go +++ b/pkg/skaffold/deploy/kpt/kpt.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deploy +package kpt import ( "bytes" @@ -33,8 +33,11 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" - deploy "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kustomize" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/types" + deployutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/util" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) @@ -47,8 +50,8 @@ const ( kptFnLocalConfig = "config.kubernetes.io/local-config" ) -// KptDeployer deploys workflows with kpt CLI -type KptDeployer struct { +// Deployer deploys workflows with kpt CLI +type Deployer struct { *latest.KptDeploy insecureRegistries map[string]bool @@ -56,19 +59,19 @@ type KptDeployer struct { globalConfig string } -func NewKptDeployer(ctx Config, labels map[string]string) *KptDeployer { - return &KptDeployer{ - KptDeploy: ctx.Pipeline().Deploy.KptDeploy, - insecureRegistries: ctx.GetInsecureRegistries(), +func NewDeployer(cfg types.Config, labels map[string]string) *Deployer { + return &Deployer{ + KptDeploy: cfg.Pipeline().Deploy.KptDeploy, + insecureRegistries: cfg.GetInsecureRegistries(), labels: labels, - globalConfig: ctx.GlobalConfig(), + globalConfig: cfg.GlobalConfig(), } } // Deploy hydrates the manifests using kustomizations and kpt functions as described in the render method, // outputs them to the applyDir, and runs `kpt live apply` against applyDir to create resources in the cluster. // `kpt live apply` supports automated pruning declaratively via resources in the applyDir. -func (k *KptDeployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact) ([]string, error) { +func (k *Deployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact) ([]string, error) { manifests, err := k.renderManifests(ctx, out, builds) if err != nil { return nil, err @@ -89,7 +92,7 @@ func (k *KptDeployer) Deploy(ctx context.Context, out io.Writer, builds []build. return nil, fmt.Errorf("getting applyDir: %w", err) } - outputRenderedManifests(manifests.String(), filepath.Join(applyDir, "resources.yaml"), out) + manifest.Write(manifests.String(), filepath.Join(applyDir, "resources.yaml"), out) cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(applyDir, []string{"live", "apply"}, k.getKptLiveApplyArgs(), nil)...) cmd.Stdout = out @@ -103,10 +106,10 @@ func (k *KptDeployer) Deploy(ctx context.Context, out io.Writer, builds []build. // Dependencies returns a list of files that the deployer depends on. This does NOT include applyDir. // In dev mode, a redeploy will be triggered if one of these files is updated. -func (k *KptDeployer) Dependencies() ([]string, error) { - deps := newStringSet() +func (k *Deployer) Dependencies() ([]string, error) { + deps := deployutil.NewStringSet() if len(k.Fn.FnPath) > 0 { - deps.insert(k.Fn.FnPath) + deps.Insert(k.Fn.FnPath) } configDeps, err := getResources(k.Dir) @@ -114,21 +117,21 @@ func (k *KptDeployer) Dependencies() ([]string, error) { return nil, fmt.Errorf("finding dependencies in %s: %w", k.Dir, err) } - deps.insert(configDeps...) + deps.Insert(configDeps...) // Kpt deployer assumes that the kustomization configuration to build lives directly under k.Dir. - kustomizeDeps, err := dependenciesForKustomization(k.Dir) + kustomizeDeps, err := kustomize.DependenciesForKustomization(k.Dir) if err != nil { return nil, fmt.Errorf("finding kustomization directly under %s: %w", k.Dir, err) } - deps.insert(kustomizeDeps...) + deps.Insert(kustomizeDeps...) - return deps.toList(), nil + return deps.ToList(), nil } // Cleanup deletes what was deployed by calling `kpt live destroy`. -func (k *KptDeployer) Cleanup(ctx context.Context, out io.Writer) error { +func (k *Deployer) Cleanup(ctx context.Context, out io.Writer) error { applyDir, err := k.getApplyDir(ctx) if err != nil { return fmt.Errorf("getting applyDir: %w", err) @@ -145,19 +148,19 @@ func (k *KptDeployer) Cleanup(ctx context.Context, out io.Writer) error { } // Render hydrates manifests using both kustomization and kpt functions. -func (k *KptDeployer) Render(ctx context.Context, out io.Writer, builds []build.Artifact, _ bool, filepath string) error { +func (k *Deployer) Render(ctx context.Context, out io.Writer, builds []build.Artifact, _ bool, filepath string) error { manifests, err := k.renderManifests(ctx, out, builds) if err != nil { return err } - return outputRenderedManifests(manifests.String(), filepath, out) + return manifest.Write(manifests.String(), filepath, out) } // renderManifests handles a majority of the hydration process for manifests. // This involves reading configs from a source directory, running kustomize build, running kpt pipelines, // adding image digests, and adding run-id labels. -func (k *KptDeployer) renderManifests(ctx context.Context, _ io.Writer, builds []build.Artifact) (deploy.ManifestList, error) { +func (k *Deployer) renderManifests(ctx context.Context, _ io.Writer, builds []build.Artifact) (manifest.ManifestList, error) { debugHelpersRegistry, err := config.GetDebugHelpersRegistry(k.globalConfig) if err != nil { return nil, fmt.Errorf("retrieving debug helpers registry: %w", err) @@ -202,11 +205,8 @@ func (k *KptDeployer) renderManifests(ctx context.Context, _ io.Writer, builds [ return nil, fmt.Errorf("replacing images in manifests: %w", err) } - for _, transform := range manifestTransforms { - manifests, err = transform(manifests, builds, Registries{k.insecureRegistries, debugHelpersRegistry}) - if err != nil { - return nil, fmt.Errorf("unable to transform manifests: %w", err) - } + if manifests, err = manifest.ApplyTransforms(manifests, builds, k.insecureRegistries, debugHelpersRegistry); err != nil { + return nil, err } return manifests.SetLabels(k.labels) @@ -214,7 +214,7 @@ func (k *KptDeployer) renderManifests(ctx context.Context, _ io.Writer, builds [ // readConfigs uses `kpt fn source` to read config manifests from k.Dir // and uses `kpt fn sink` to output those manifests to .pipeline. -func (k *KptDeployer) readConfigs(ctx context.Context) error { +func (k *Deployer) readConfigs(ctx context.Context) error { cmd := exec.CommandContext(ctx, "kpt", kptCommandArgs(k.Dir, []string{"fn", "source"}, nil, nil)...) b, err := util.RunCmdOut(cmd) if err != nil { @@ -231,18 +231,18 @@ func (k *KptDeployer) readConfigs(ctx context.Context) error { } // kustomizeBuild runs `kustomize build` if a kustomization config exists and outputs to .pipeline. -func (k *KptDeployer) kustomizeBuild(ctx context.Context) error { - if _, err := findKustomizationConfig(k.Dir); err != nil { +func (k *Deployer) kustomizeBuild(ctx context.Context) error { + if _, err := kustomize.FindKustomizationConfig(k.Dir); err != nil { // No kustomization config was found directly under k.Dir, so there is no need to continue. return nil } - cmd := exec.CommandContext(ctx, "kustomize", append([]string{"build"}, buildCommandArgs([]string{"-o", filepath.Join(pipeline, k.Dir)}, k.Dir)...)...) + cmd := exec.CommandContext(ctx, "kustomize", append([]string{"build"}, kustomize.BuildCommandArgs([]string{"-o", filepath.Join(pipeline, k.Dir)}, k.Dir)...)...) if _, err := util.RunCmdOut(cmd); err != nil { return err } - deps, err := dependenciesForKustomization(k.Dir) + deps, err := kustomize.DependenciesForKustomization(k.Dir) if err != nil { return fmt.Errorf("finding kustomization dependencies: %w", err) } @@ -260,8 +260,8 @@ func (k *KptDeployer) kustomizeBuild(ctx context.Context) error { // kptFnRun does a dry run with the specified kpt functions (fn-path XOR image) against .pipeline. // If neither fn-path nor image are specified, functions will attempt to be discovered in .pipeline. // An error occurs if both fn-path and image are specified. -func (k *KptDeployer) kptFnRun(ctx context.Context) (deploy.ManifestList, error) { - var manifests deploy.ManifestList +func (k *Deployer) kptFnRun(ctx context.Context) (manifest.ManifestList, error) { + var manifests manifest.ManifestList flags, err := k.getKptFnRunArgs() if err != nil { @@ -283,9 +283,9 @@ func (k *KptDeployer) kptFnRun(ctx context.Context) (deploy.ManifestList, error) // excludeKptFn adds an annotation "config.kubernetes.io/local-config: 'true'" to kpt function. // This will exclude kpt functions from deployed to the cluster in kpt live apply. -func (k *KptDeployer) excludeKptFn(manifest deploy.ManifestList) (deploy.ManifestList, error) { - var newManifest deploy.ManifestList - for _, yByte := range manifest { +func (k *Deployer) excludeKptFn(originalManifest manifest.ManifestList) (manifest.ManifestList, error) { + var newManifest manifest.ManifestList + for _, yByte := range originalManifest { // Convert yaml byte config to unstructured.Unstructured jByte, _ := k8syaml.YAMLToJSON(yByte) var obj unstructured.Unstructured @@ -322,7 +322,7 @@ func (k *KptDeployer) excludeKptFn(manifest deploy.ManifestList) (deploy.Manifes // getApplyDir returns the path to applyDir if specified by the user. Otherwise, getApplyDir // creates a hidden directory named .kpt-hydrated in place of applyDir. -func (k *KptDeployer) getApplyDir(ctx context.Context) (string, error) { +func (k *Deployer) getApplyDir(ctx context.Context) (string, error) { if k.Live.Apply.Dir != "" { if _, err := os.Stat(k.Live.Apply.Dir); os.IsNotExist(err) { return "", err @@ -399,7 +399,7 @@ func getResources(root string) ([]string, error) { } // getKptFnRunArgs returns a list of arguments that the user specified for the `kpt fn run` command. -func (k *KptDeployer) getKptFnRunArgs() ([]string, error) { +func (k *Deployer) getKptFnRunArgs() ([]string, error) { // --dry-run sets the pipeline's output to STDOUT, otherwise output is set to sinkDir. // For now, k.Dir will be treated as sinkDir (and sourceDir). flags := []string{"--dry-run"} @@ -440,7 +440,7 @@ func (k *KptDeployer) getKptFnRunArgs() ([]string, error) { } // getKptLiveApplyArgs returns a list of arguments that the user specified for the `kpt live apply` command. -func (k *KptDeployer) getKptLiveApplyArgs() []string { +func (k *Deployer) getKptLiveApplyArgs() []string { var flags []string if len(k.Live.Options.PollPeriod) > 0 { @@ -463,7 +463,7 @@ func (k *KptDeployer) getKptLiveApplyArgs() []string { } // getKptLiveInitArgs returns a list of arguments that the user specified for the `kpt live init` command. -func (k *KptDeployer) getKptLiveInitArgs() []string { +func (k *Deployer) getKptLiveInitArgs() []string { var flags []string if len(k.Live.Apply.InventoryID) > 0 { diff --git a/pkg/skaffold/deploy/kpt_test.go b/pkg/skaffold/deploy/kpt/kpt_test.go similarity index 96% rename from pkg/skaffold/deploy/kpt_test.go rename to pkg/skaffold/deploy/kpt/kpt_test.go index 127d89a1ddf..603342dbdd5 100644 --- a/pkg/skaffold/deploy/kpt_test.go +++ b/pkg/skaffold/deploy/kpt/kpt_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deploy +package kpt import ( "bytes" @@ -28,7 +28,8 @@ import ( "testing" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" - deploy "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" @@ -213,7 +214,7 @@ spec: tmpDir.WriteFiles(test.kustomizations) - k := NewKptDeployer(&kptConfig{ + k := NewDeployer(&kptConfig{ kpt: test.kpt, }, nil) @@ -338,7 +339,7 @@ func TestKpt_Dependencies(t *testing.T) { tmpDir.WriteFiles(test.createFiles) tmpDir.WriteFiles(test.kustomizations) - k := NewKptDeployer(&kptConfig{ + k := NewDeployer(&kptConfig{ kpt: test.kpt, }, nil) @@ -391,7 +392,7 @@ func TestKpt_Cleanup(t *testing.T) { os.Mkdir(test.applyDir, 0755) } - k := NewKptDeployer(&kptConfig{ + k := NewDeployer(&kptConfig{ workingDir: ".", kpt: latest.KptDeploy{ Live: latest.KptLive{ @@ -744,7 +745,7 @@ spec: tmpDir.WriteFiles(test.kustomizations) - k := NewKptDeployer(&kptConfig{ + k := NewDeployer(&kptConfig{ workingDir: ".", kpt: test.kpt, }, test.labels) @@ -819,7 +820,7 @@ func TestKpt_GetApplyDir(t *testing.T) { tmpDir.Touch(".kpt-hydrated/inventory-template.yaml") } - k := NewKptDeployer(&kptConfig{ + k := NewDeployer(&kptConfig{ workingDir: ".", kpt: latest.KptDeploy{ Live: test.live, @@ -921,13 +922,13 @@ spec: name: image1`) tests := []struct { description string - manifests deploy.ManifestList - expected deploy.ManifestList + manifests manifest.ManifestList + expected manifest.ManifestList }{ { description: "Add `local-config` annotation to kpt fn", - manifests: deploy.ManifestList{testFn1}, - expected: deploy.ManifestList{[]byte(`apiVersion: v1 + manifests: manifest.ManifestList{testFn1}, + expected: manifest.ManifestList{[]byte(`apiVersion: v1 data: annotation_name: k1 annotation_value: v1 @@ -939,8 +940,8 @@ metadata: }, { description: "Skip preset `local-config` annotation", - manifests: deploy.ManifestList{testFn2}, - expected: deploy.ManifestList{[]byte(`apiVersion: v1 + manifests: manifest.ManifestList{testFn2}, + expected: manifest.ManifestList{[]byte(`apiVersion: v1 kind: ConfigMap metadata: annotations: @@ -952,8 +953,8 @@ data: }, { description: "Valid in kpt fn pipeline.", - manifests: deploy.ManifestList{testFn1, testFn2, testPod}, - expected: deploy.ManifestList{[]byte(`apiVersion: v1 + manifests: manifest.ManifestList{testFn1, testFn2, testPod}, + expected: manifest.ManifestList{[]byte(`apiVersion: v1 data: annotation_name: k1 annotation_value: v1 @@ -981,7 +982,7 @@ spec: } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - k := NewKptDeployer(&kptConfig{}, nil) + k := NewDeployer(&kptConfig{}, nil) actualManifest, err := k.excludeKptFn(test.manifests) t.CheckErrorAndDeepEqual(false, err, test.expected.String(), actualManifest.String()) }) @@ -995,8 +996,8 @@ type kptConfig struct { } func (c *kptConfig) WorkingDir() string { return c.workingDir } -func (c *kptConfig) GetKubeContext() string { return testKubeContext } -func (c *kptConfig) GetKubeNamespace() string { return testNamespace } +func (c *kptConfig) GetKubeContext() string { return kubectl.TestKubeContext } +func (c *kptConfig) GetKubeNamespace() string { return kubectl.TestNamespace } func (c *kptConfig) Pipeline() latest.Pipeline { var pipeline latest.Pipeline pipeline.Deploy.DeployType.KptDeploy = &c.kpt diff --git a/pkg/skaffold/deploy/kubectl/cli.go b/pkg/skaffold/deploy/kubectl/cli.go index 93b2156aa47..13b475965be 100644 --- a/pkg/skaffold/deploy/kubectl/cli.go +++ b/pkg/skaffold/deploy/kubectl/cli.go @@ -27,22 +27,25 @@ import ( "github.com/sirupsen/logrus" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" - pkgkubectl "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" + deploy "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/types" + kubectl "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" ) // CLI holds parameters to run kubectl. type CLI struct { - *pkgkubectl.CLI + *kubectl.CLI Flags latest.KubectlFlags forceDeploy bool waitForDeletions config.WaitForDeletions - previousApply ManifestList + previousApply manifest.ManifestList } type Config interface { - pkgkubectl.Config + kubectl.Config + deploy.Config ForceDeploy() bool WaitForDeletions() config.WaitForDeletions Mode() config.RunMode @@ -50,7 +53,7 @@ type Config interface { func NewCLI(cfg Config, flags latest.KubectlFlags, defaultNameSpace string) CLI { return CLI{ - CLI: pkgkubectl.NewCLI(cfg, defaultNameSpace), + CLI: kubectl.NewCLI(cfg, defaultNameSpace), Flags: flags, forceDeploy: cfg.ForceDeploy(), waitForDeletions: cfg.WaitForDeletions(), @@ -58,7 +61,7 @@ func NewCLI(cfg Config, flags latest.KubectlFlags, defaultNameSpace string) CLI } // Delete runs `kubectl delete` on a list of manifests. -func (c *CLI) Delete(ctx context.Context, out io.Writer, manifests ManifestList) error { +func (c *CLI) Delete(ctx context.Context, out io.Writer, manifests manifest.ManifestList) error { args := c.args(c.Flags.Delete, "--ignore-not-found=true", "-f", "-") if err := c.Run(ctx, manifests.Reader(), out, "delete", args...); err != nil { return fmt.Errorf("kubectl delete: %w", err) @@ -68,7 +71,7 @@ func (c *CLI) Delete(ctx context.Context, out io.Writer, manifests ManifestList) } // Apply runs `kubectl apply` on a list of manifests. -func (c *CLI) Apply(ctx context.Context, out io.Writer, manifests ManifestList) error { +func (c *CLI) Apply(ctx context.Context, out io.Writer, manifests manifest.ManifestList) error { // Only redeploy modified or new manifests // TODO(dgageot): should we delete a manifest that was deployed and is not anymore? updated := c.previousApply.Diff(manifests) @@ -109,7 +112,7 @@ type getResult struct { } // WaitForDeletions waits for resource marked for deletion to complete their deletion. -func (c *CLI) WaitForDeletions(ctx context.Context, out io.Writer, manifests ManifestList) error { +func (c *CLI) WaitForDeletions(ctx context.Context, out io.Writer, manifests manifest.ManifestList) error { if !c.waitForDeletions.Enabled { return nil } @@ -174,7 +177,7 @@ func (c *CLI) WaitForDeletions(ctx context.Context, out io.Writer, manifests Man } // ReadManifests reads a list of manifests in yaml format. -func (c *CLI) ReadManifests(ctx context.Context, manifests []string) (ManifestList, error) { +func (c *CLI) ReadManifests(ctx context.Context, manifests []string) (manifest.ManifestList, error) { var list []string for _, manifest := range manifests { list = append(list, "-f", manifest) @@ -199,7 +202,7 @@ func (c *CLI) ReadManifests(ctx context.Context, manifests []string) (ManifestLi return nil, fmt.Errorf("kubectl create: %w", err) } - var manifestList ManifestList + var manifestList manifest.ManifestList manifestList.Append(buf) return manifestList, nil diff --git a/pkg/skaffold/deploy/kubectl/constants.go b/pkg/skaffold/deploy/kubectl/constants.go new file mode 100644 index 00000000000..ca374fcc267 --- /dev/null +++ b/pkg/skaffold/deploy/kubectl/constants.go @@ -0,0 +1,73 @@ +/* +Copyright 2020 The Skaffold Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kubectl + +const ( + KubectlVersion112 = `{"clientVersion":{"major":"1","minor":"12"}}` + KubectlVersion118 = `{"clientVersion":{"major":"1","minor":"18"}}` +) + +var TestKubeConfig = "kubeconfig" +var TestKubeContext = "kubecontext" +var TestNamespace = "testNamespace" +var TestNamespace2 = "testNamespace2" +var TestNamespace2FromEnvTemplate = "test{{.MYENV}}ace2" // needs `MYENV=Namesp` environment variable + +const DeploymentWebYAML = `apiVersion: v1 +kind: Pod +metadata: + name: leeroy-web +spec: + containers: + - name: leeroy-web + image: leeroy-web` + +const DeploymentWebYAMLv1 = `apiVersion: v1 +kind: Pod +metadata: + name: leeroy-web +spec: + containers: + - image: leeroy-web:v1 + name: leeroy-web` + +const DeploymentAppYAML = `apiVersion: v1 +kind: Pod +metadata: + name: leeroy-app +spec: + containers: + - name: leeroy-app + image: leeroy-app` + +const DeploymentAppYAMLv1 = `apiVersion: v1 +kind: Pod +metadata: + name: leeroy-app +spec: + containers: + - image: leeroy-app:v1 + name: leeroy-app` + +const DeploymentAppYAMLv2 = `apiVersion: v1 +kind: Pod +metadata: + name: leeroy-app +spec: + containers: + - image: leeroy-app:v2 + name: leeroy-app` diff --git a/pkg/skaffold/deploy/kubectl.go b/pkg/skaffold/deploy/kubectl/kubectl.go similarity index 78% rename from pkg/skaffold/deploy/kubectl.go rename to pkg/skaffold/deploy/kubectl/kubectl.go index 8da56963b4e..a2ecf9d9bc6 100644 --- a/pkg/skaffold/deploy/kubectl.go +++ b/pkg/skaffold/deploy/kubectl/kubectl.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deploy +package kubectl import ( "bytes" @@ -32,42 +32,32 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" - deploy "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" + deployutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/util" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" ) -// KubectlDeployer deploys workflows using kubectl CLI. -type KubectlDeployer struct { +// Deployer deploys workflows using kubectl CLI. +type Deployer struct { *latest.KubectlDeploy originalImages []build.Artifact workingDir string globalConfig string + gcsManifestDir string defaultRepo *string - kubectl deploy.CLI + kubectl CLI insecureRegistries map[string]bool labels map[string]string skipRender bool } -type Config interface { - deploy.Config - docker.Config - - Pipeline() latest.Pipeline - GetWorkingDir() string - GlobalConfig() string - DefaultRepo() *string - SkipRender() bool -} - -// NewKubectlDeployer returns a new KubectlDeployer for a DeployConfig filled +// NewDeployer returns a new Deployer for a DeployConfig filled // with the needed configuration for `kubectl apply` -func NewKubectlDeployer(cfg Config, labels map[string]string) (*KubectlDeployer, error) { +func NewDeployer(cfg Config, labels map[string]string) (*Deployer, error) { defaultNamespace := "" if cfg.Pipeline().Deploy.KubectlDeploy.DefaultNamespace != nil { var err error @@ -77,12 +67,12 @@ func NewKubectlDeployer(cfg Config, labels map[string]string) (*KubectlDeployer, } } - return &KubectlDeployer{ + return &Deployer{ KubectlDeploy: cfg.Pipeline().Deploy.KubectlDeploy, workingDir: cfg.GetWorkingDir(), globalConfig: cfg.GlobalConfig(), defaultRepo: cfg.DefaultRepo(), - kubectl: deploy.NewCLI(cfg, cfg.Pipeline().Deploy.KubectlDeploy.Flags, defaultNamespace), + kubectl: NewCLI(cfg, cfg.Pipeline().Deploy.KubectlDeploy.Flags, defaultNamespace), insecureRegistries: cfg.GetInsecureRegistries(), skipRender: cfg.SkipRender(), labels: labels, @@ -91,9 +81,9 @@ func NewKubectlDeployer(cfg Config, labels map[string]string) (*KubectlDeployer, // Deploy templates the provided manifests with a simple `find and replace` and // runs `kubectl apply` on those manifests -func (k *KubectlDeployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact) ([]string, error) { +func (k *Deployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact) ([]string, error) { var ( - manifests deploy.ManifestList + manifests manifest.ManifestList err error ) if k.skipRender { @@ -126,7 +116,7 @@ func (k *KubectlDeployer) Deploy(ctx context.Context, out io.Writer, builds []bu return namespaces, nil } -func (k *KubectlDeployer) manifestFiles(manifests []string) ([]string, error) { +func (k *Deployer) manifestFiles(manifests []string) ([]string, error) { var nonURLManifests, gcsManifests []string for _, manifest := range manifests { switch { @@ -145,10 +135,11 @@ func (k *KubectlDeployer) manifestFiles(manifests []string) ([]string, error) { if len(gcsManifests) != 0 { // return tmp dir of the downloaded manifests - tmpDir, err := downloadManifestsFromGCS(gcsManifests) + tmpDir, err := manifest.DownloadFromGCS(gcsManifests) if err != nil { return nil, fmt.Errorf("downloading from GCS: %w", err) } + k.gcsManifestDir = tmpDir l, err := util.ExpandPathsGlob(tmpDir, []string{"*"}) if err != nil { return nil, fmt.Errorf("expanding kubectl manifest paths: %w", err) @@ -172,11 +163,11 @@ func (k *KubectlDeployer) manifestFiles(manifests []string) ([]string, error) { } // readManifests reads the manifests to deploy/delete. -func (k *KubectlDeployer) readManifests(ctx context.Context, offline bool) (deploy.ManifestList, error) { +func (k *Deployer) readManifests(ctx context.Context, offline bool) (manifest.ManifestList, error) { // Get file manifests manifests, err := k.Dependencies() // Clean the temporary directory that holds the manifests downloaded from GCS - defer os.RemoveAll(manifestTmpDir) + defer os.RemoveAll(k.gcsManifestDir) if err != nil { return nil, fmt.Errorf("listing manifests: %w", err) @@ -192,7 +183,7 @@ func (k *KubectlDeployer) readManifests(ctx context.Context, offline bool) (depl } if len(manifests) == 0 { - return deploy.ManifestList{}, nil + return manifest.ManifestList{}, nil } if !offline { @@ -205,7 +196,7 @@ func (k *KubectlDeployer) readManifests(ctx context.Context, offline bool) (depl return nil, errors.New("cannot use offline mode if URL manifests are configured") } - var manifestList deploy.ManifestList + var manifestList manifest.ManifestList for _, manifestFilePath := range manifests { manifestFileContent, err := ioutil.ReadFile(manifestFilePath) if err != nil { @@ -218,7 +209,7 @@ func (k *KubectlDeployer) readManifests(ctx context.Context, offline bool) (depl // readRemoteManifests will try to read manifests from the given kubernetes // context in the specified namespace and for the specified type -func (k *KubectlDeployer) readRemoteManifest(ctx context.Context, name string) ([]byte, error) { +func (k *Deployer) readRemoteManifest(ctx context.Context, name string) ([]byte, error) { var args []string ns := "" if parts := strings.Split(name, ":"); len(parts) > 1 { @@ -236,16 +227,16 @@ func (k *KubectlDeployer) readRemoteManifest(ctx context.Context, name string) ( return manifest.Bytes(), nil } -func (k *KubectlDeployer) Render(ctx context.Context, out io.Writer, builds []build.Artifact, offline bool, filepath string) error { +func (k *Deployer) Render(ctx context.Context, out io.Writer, builds []build.Artifact, offline bool, filepath string) error { manifests, err := k.renderManifests(ctx, out, builds, offline) if err != nil { return err } - return outputRenderedManifests(manifests.String(), filepath, out) + return manifest.Write(manifests.String(), filepath, out) } -func (k *KubectlDeployer) renderManifests(ctx context.Context, out io.Writer, builds []build.Artifact, offline bool) (deploy.ManifestList, error) { +func (k *Deployer) renderManifests(ctx context.Context, out io.Writer, builds []build.Artifact, offline bool) (manifest.ManifestList, error) { if err := k.kubectl.CheckVersion(ctx); err != nil { color.Default.Fprintln(out, "kubectl client version:", k.kubectl.Version(ctx)) color.Default.Fprintln(out, err) @@ -283,7 +274,7 @@ func (k *KubectlDeployer) renderManifests(ctx context.Context, out io.Writer, bu if len(builds) == 0 { for _, artifact := range k.originalImages { - tag, err := ApplyDefaultRepo(k.globalConfig, k.defaultRepo, artifact.Tag) + tag, err := deployutil.ApplyDefaultRepo(k.globalConfig, k.defaultRepo, artifact.Tag) if err != nil { return nil, err } @@ -296,21 +287,18 @@ func (k *KubectlDeployer) renderManifests(ctx context.Context, out io.Writer, bu manifests, err = manifests.ReplaceImages(builds) if err != nil { - return nil, fmt.Errorf("replacing images in manifests: %w", err) + return nil, err } - for _, transform := range manifestTransforms { - manifests, err = transform(manifests, builds, Registries{k.insecureRegistries, debugHelpersRegistry}) - if err != nil { - return nil, fmt.Errorf("unable to transform manifests: %w", err) - } + if manifests, err = manifest.ApplyTransforms(manifests, builds, k.insecureRegistries, debugHelpersRegistry); err != nil { + return nil, err } return manifests.SetLabels(k.labels) } // Cleanup deletes what was deployed by calling Deploy. -func (k *KubectlDeployer) Cleanup(ctx context.Context, out io.Writer) error { +func (k *Deployer) Cleanup(ctx context.Context, out io.Writer) error { manifests, err := k.readManifests(ctx, false) if err != nil { return fmt.Errorf("reading manifests: %w", err) @@ -320,7 +308,7 @@ func (k *KubectlDeployer) Cleanup(ctx context.Context, out io.Writer) error { // TODO(dgageot): That seems super dangerous and I don't understand // why we need to update resources just before we delete them. if len(k.RemoteManifests) > 0 { - var rm deploy.ManifestList + var rm manifest.ManifestList for _, m := range k.RemoteManifests { manifest, err := k.readRemoteManifest(ctx, m) if err != nil { @@ -347,6 +335,6 @@ func (k *KubectlDeployer) Cleanup(ctx context.Context, out io.Writer) error { } // Dependencies lists all the files that describe what needs to be deployed. -func (k *KubectlDeployer) Dependencies() ([]string, error) { +func (k *Deployer) Dependencies() ([]string, error) { return k.manifestFiles(k.KubectlDeploy.Manifests) } diff --git a/pkg/skaffold/deploy/kubectl_test.go b/pkg/skaffold/deploy/kubectl/kubectl_test.go similarity index 78% rename from pkg/skaffold/deploy/kubectl_test.go rename to pkg/skaffold/deploy/kubectl/kubectl_test.go index 2b9e762676e..aaa8889f92a 100644 --- a/pkg/skaffold/deploy/kubectl_test.go +++ b/pkg/skaffold/deploy/kubectl/kubectl_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deploy +package kubectl import ( "bytes" @@ -29,66 +29,13 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/testutil" ) -const ( - testKubeContext = "kubecontext" - testKubeConfig = "kubeconfig" - kubectlVersion112 = `{"clientVersion":{"major":"1","minor":"12"}}` - kubectlVersion118 = `{"clientVersion":{"major":"1","minor":"18"}}` -) - -var testNamespace2FromEnvTemplate = "test{{.MYENV}}ace2" // needs `MYENV=Namesp` environment variable - -const deploymentWebYAML = `apiVersion: v1 -kind: Pod -metadata: - name: leeroy-web -spec: - containers: - - name: leeroy-web - image: leeroy-web` - -const deploymentWebYAMLv1 = `apiVersion: v1 -kind: Pod -metadata: - name: leeroy-web -spec: - containers: - - image: leeroy-web:v1 - name: leeroy-web` - -const deploymentAppYAML = `apiVersion: v1 -kind: Pod -metadata: - name: leeroy-app -spec: - containers: - - name: leeroy-app - image: leeroy-app` - -const deploymentAppYAMLv1 = `apiVersion: v1 -kind: Pod -metadata: - name: leeroy-app -spec: - containers: - - image: leeroy-app:v1 - name: leeroy-app` - -const deploymentAppYAMLv2 = `apiVersion: v1 -kind: Pod -metadata: - name: leeroy-app -spec: - containers: - - image: leeroy-app:v2 - name: leeroy-app` - func TestKubectlDeploy(t *testing.T) { tests := []struct { description string @@ -104,7 +51,7 @@ func TestKubectlDeploy(t *testing.T) { { description: "no manifest", kubectl: latest.KubectlDeploy{}, - commands: testutil.CmdRunOut("kubectl version --client -ojson", kubectlVersion112), + commands: testutil.CmdRunOut("kubectl version --client -ojson", KubectlVersion112), waitForDeletions: true, }, { @@ -116,9 +63,9 @@ func TestKubectlDeploy(t *testing.T) { }, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion112). - AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f deployment.yaml --validate=false", deploymentWebYAML). - AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, ""). + CmdRunOut("kubectl version --client -ojson", KubectlVersion112). + AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f deployment.yaml --validate=false", DeploymentWebYAML). + AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", DeploymentWebYAMLv1, ""). AndRun("kubectl --context kubecontext --namespace testNamespace apply -f - --validate=false"), builds: []build.Artifact{{ ImageName: "leeroy-web", @@ -132,9 +79,9 @@ func TestKubectlDeploy(t *testing.T) { Manifests: []string{"deployment.yaml"}, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion112). - AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f deployment.yaml", deploymentWebYAML). - AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, ""). + CmdRunOut("kubectl version --client -ojson", KubectlVersion112). + AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f deployment.yaml", DeploymentWebYAML). + AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", DeploymentWebYAMLv1, ""). AndRun("kubectl --context kubecontext --namespace testNamespace apply -f - --force --grace-period=0"), builds: []build.Artifact{{ ImageName: "leeroy-web", @@ -149,9 +96,9 @@ func TestKubectlDeploy(t *testing.T) { Manifests: []string{"deployment.yaml"}, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion112). - AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f deployment.yaml", deploymentWebYAML). - AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, ""). + CmdRunOut("kubectl version --client -ojson", KubectlVersion112). + AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f deployment.yaml", DeploymentWebYAML). + AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", DeploymentWebYAMLv1, ""). AndRun("kubectl --context kubecontext --namespace testNamespace apply -f -"), builds: []build.Artifact{{ ImageName: "leeroy-web", @@ -165,9 +112,9 @@ func TestKubectlDeploy(t *testing.T) { Manifests: []string{"deployment.yaml"}, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion118). - AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run=client -oyaml -f deployment.yaml", deploymentWebYAML). - AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, ""). + CmdRunOut("kubectl version --client -ojson", KubectlVersion118). + AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run=client -oyaml -f deployment.yaml", DeploymentWebYAML). + AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", DeploymentWebYAMLv1, ""). AndRun("kubectl --context kubecontext --namespace testNamespace apply -f -"), builds: []build.Artifact{{ ImageName: "leeroy-web", @@ -179,12 +126,12 @@ func TestKubectlDeploy(t *testing.T) { description: "deploy success (default namespace)", kubectl: latest.KubectlDeploy{ Manifests: []string{"deployment.yaml"}, - DefaultNamespace: &testNamespace2, + DefaultNamespace: &TestNamespace2, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion118). - AndRunOut("kubectl --context kubecontext --namespace testNamespace2 create --dry-run=client -oyaml -f deployment.yaml", deploymentWebYAML). - AndRunInputOut("kubectl --context kubecontext --namespace testNamespace2 get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, ""). + CmdRunOut("kubectl version --client -ojson", KubectlVersion118). + AndRunOut("kubectl --context kubecontext --namespace testNamespace2 create --dry-run=client -oyaml -f deployment.yaml", DeploymentWebYAML). + AndRunInputOut("kubectl --context kubecontext --namespace testNamespace2 get -f - --ignore-not-found -ojson", DeploymentWebYAMLv1, ""). AndRun("kubectl --context kubecontext --namespace testNamespace2 apply -f -"), builds: []build.Artifact{{ ImageName: "leeroy-web", @@ -197,12 +144,12 @@ func TestKubectlDeploy(t *testing.T) { description: "deploy success (default namespace with env template)", kubectl: latest.KubectlDeploy{ Manifests: []string{"deployment.yaml"}, - DefaultNamespace: &testNamespace2FromEnvTemplate, + DefaultNamespace: &TestNamespace2FromEnvTemplate, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion118). - AndRunOut("kubectl --context kubecontext --namespace testNamespace2 create --dry-run=client -oyaml -f deployment.yaml", deploymentWebYAML). - AndRunInputOut("kubectl --context kubecontext --namespace testNamespace2 get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, ""). + CmdRunOut("kubectl version --client -ojson", KubectlVersion118). + AndRunOut("kubectl --context kubecontext --namespace testNamespace2 create --dry-run=client -oyaml -f deployment.yaml", DeploymentWebYAML). + AndRunInputOut("kubectl --context kubecontext --namespace testNamespace2 get -f - --ignore-not-found -ojson", DeploymentWebYAMLv1, ""). AndRun("kubectl --context kubecontext --namespace testNamespace2 apply -f -"), builds: []build.Artifact{{ ImageName: "leeroy-web", @@ -220,9 +167,9 @@ func TestKubectlDeploy(t *testing.T) { Manifests: []string{"deployment.yaml", "http://remote.yaml"}, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion112). - AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f deployment.yaml -f http://remote.yaml", deploymentWebYAML). - AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, ""). + CmdRunOut("kubectl version --client -ojson", KubectlVersion112). + AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f deployment.yaml -f http://remote.yaml", DeploymentWebYAML). + AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", DeploymentWebYAMLv1, ""). AndRun("kubectl --context kubecontext --namespace testNamespace apply -f -"), builds: []build.Artifact{{ ImageName: "leeroy-web", @@ -236,9 +183,9 @@ func TestKubectlDeploy(t *testing.T) { Manifests: []string{"deployment.yaml"}, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion112). - AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f deployment.yaml", deploymentWebYAML). - AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, ""). + CmdRunOut("kubectl version --client -ojson", KubectlVersion112). + AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f deployment.yaml", DeploymentWebYAML). + AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", DeploymentWebYAMLv1, ""). AndRunErr("kubectl --context kubecontext --namespace testNamespace apply -f -", fmt.Errorf("")), builds: []build.Artifact{{ ImageName: "leeroy-web", @@ -258,9 +205,9 @@ func TestKubectlDeploy(t *testing.T) { }, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion112). - AndRunOut("kubectl --context kubecontext --namespace testNamespace create -v=0 --dry-run -oyaml -f deployment.yaml", deploymentWebYAML). - AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -v=0 -f - --ignore-not-found -ojson", deploymentWebYAMLv1, ""). + CmdRunOut("kubectl version --client -ojson", KubectlVersion112). + AndRunOut("kubectl --context kubecontext --namespace testNamespace create -v=0 --dry-run -oyaml -f deployment.yaml", DeploymentWebYAML). + AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -v=0 -f - --ignore-not-found -ojson", DeploymentWebYAMLv1, ""). AndRunErr("kubectl --context kubecontext --namespace testNamespace apply -v=0 --overwrite=true -f -", fmt.Errorf("")), builds: []build.Artifact{{ ImageName: "leeroy-web", @@ -275,16 +222,16 @@ func TestKubectlDeploy(t *testing.T) { t.SetEnvs(test.envs) t.Override(&util.DefaultExecCommand, test.commands) t.NewTempDir(). - Write("deployment.yaml", deploymentWebYAML). + Write("deployment.yaml", DeploymentWebYAML). Touch("empty.ignored"). Chdir() skaffoldNamespaceOption := "" if !test.skipSkaffoldNamespaceOption { - skaffoldNamespaceOption = testNamespace + skaffoldNamespaceOption = TestNamespace } - k, err := NewKubectlDeployer(&kubectlConfig{ + k, err := NewDeployer(&kubectlConfig{ workingDir: ".", kubectl: test.kubectl, force: test.forceDeploy, @@ -318,8 +265,8 @@ func TestKubectlCleanup(t *testing.T) { Manifests: []string{"deployment.yaml"}, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion112). - AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f deployment.yaml", deploymentWebYAML). + CmdRunOut("kubectl version --client -ojson", KubectlVersion112). + AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f deployment.yaml", DeploymentWebYAML). AndRun("kubectl --context kubecontext --namespace testNamespace delete --ignore-not-found=true -f -"), }, { @@ -328,8 +275,8 @@ func TestKubectlCleanup(t *testing.T) { Manifests: []string{"deployment.yaml"}, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion118). - AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run=client -oyaml -f deployment.yaml", deploymentWebYAML). + CmdRunOut("kubectl version --client -ojson", KubectlVersion118). + AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run=client -oyaml -f deployment.yaml", DeploymentWebYAML). AndRun("kubectl --context kubecontext --namespace testNamespace delete --ignore-not-found=true -f -"), }, { @@ -338,8 +285,8 @@ func TestKubectlCleanup(t *testing.T) { Manifests: []string{"deployment.yaml"}, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion112). - AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f deployment.yaml", deploymentWebYAML). + CmdRunOut("kubectl version --client -ojson", KubectlVersion112). + AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f deployment.yaml", DeploymentWebYAML). AndRunErr("kubectl --context kubecontext --namespace testNamespace delete --ignore-not-found=true -f -", errors.New("BUG")), shouldErr: true, }, @@ -354,8 +301,8 @@ func TestKubectlCleanup(t *testing.T) { }, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion112). - AndRunOut("kubectl --context kubecontext --namespace testNamespace create -v=0 --dry-run -oyaml -f deployment.yaml", deploymentWebYAML). + CmdRunOut("kubectl version --client -ojson", KubectlVersion112). + AndRunOut("kubectl --context kubecontext --namespace testNamespace create -v=0 --dry-run -oyaml -f deployment.yaml", DeploymentWebYAML). AndRun("kubectl --context kubecontext --namespace testNamespace delete -v=0 --grace-period=1 --ignore-not-found=true -f -"), }, } @@ -363,13 +310,13 @@ func TestKubectlCleanup(t *testing.T) { testutil.Run(t, test.description, func(t *testutil.T) { t.Override(&util.DefaultExecCommand, test.commands) t.NewTempDir(). - Write("deployment.yaml", deploymentWebYAML). + Write("deployment.yaml", DeploymentWebYAML). Chdir() - k, err := NewKubectlDeployer(&kubectlConfig{ + k, err := NewDeployer(&kubectlConfig{ workingDir: ".", kubectl: test.kubectl, - RunContext: runcontext.RunContext{Opts: config.SkaffoldOptions{Namespace: testNamespace}}, + RunContext: runcontext.RunContext{Opts: config.SkaffoldOptions{Namespace: TestNamespace}}, }, nil) t.RequireNoError(err) @@ -394,7 +341,7 @@ func TestKubectlDeployerRemoteCleanup(t *testing.T) { commands: testutil. CmdRun("kubectl --context kubecontext --namespace testNamespace get pod/leeroy-web -o yaml"). AndRun("kubectl --context kubecontext --namespace testNamespace delete --ignore-not-found=true -f -"). - AndRunInput("kubectl --context kubecontext --namespace testNamespace apply -f -", deploymentWebYAML), + AndRunInput("kubectl --context kubecontext --namespace testNamespace apply -f -", DeploymentWebYAML), }, { description: "cleanup error", @@ -404,20 +351,20 @@ func TestKubectlDeployerRemoteCleanup(t *testing.T) { commands: testutil. CmdRun("kubectl --context kubecontext --namespace anotherNamespace get pod/leeroy-web -o yaml"). AndRun("kubectl --context kubecontext --namespace testNamespace delete --ignore-not-found=true -f -"). - AndRunInput("kubectl --context kubecontext --namespace anotherNamespace apply -f -", deploymentWebYAML), + AndRunInput("kubectl --context kubecontext --namespace anotherNamespace apply -f -", DeploymentWebYAML), }, } for _, test := range tests { testutil.Run(t, "cleanup remote", func(t *testutil.T) { t.Override(&util.DefaultExecCommand, test.commands) t.NewTempDir(). - Write("deployment.yaml", deploymentWebYAML). + Write("deployment.yaml", DeploymentWebYAML). Chdir() - k, err := NewKubectlDeployer(&kubectlConfig{ + k, err := NewDeployer(&kubectlConfig{ workingDir: ".", kubectl: test.kubectl, - RunContext: runcontext.RunContext{Opts: config.SkaffoldOptions{Namespace: testNamespace}}, + RunContext: runcontext.RunContext{Opts: config.SkaffoldOptions{Namespace: TestNamespace}}, }, nil) t.RequireNoError(err) @@ -431,22 +378,22 @@ func TestKubectlDeployerRemoteCleanup(t *testing.T) { func TestKubectlRedeploy(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { tmpDir := t.NewTempDir(). - Write("deployment-web.yaml", deploymentWebYAML). - Write("deployment-app.yaml", deploymentAppYAML) + Write("deployment-web.yaml", DeploymentWebYAML). + Write("deployment-app.yaml", DeploymentAppYAML) t.Override(&util.DefaultExecCommand, testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion112). - AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f "+tmpDir.Path("deployment-app.yaml")+" -f "+tmpDir.Path("deployment-web.yaml"), deploymentAppYAML+"\n"+deploymentWebYAML). - AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", deploymentAppYAMLv1+"\n---\n"+deploymentWebYAMLv1, ""). - AndRunInput("kubectl --context kubecontext apply -f -", deploymentAppYAMLv1+"\n---\n"+deploymentWebYAMLv1). - AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f "+tmpDir.Path("deployment-app.yaml")+" -f "+tmpDir.Path("deployment-web.yaml"), deploymentAppYAML+"\n"+deploymentWebYAML). - AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", deploymentAppYAMLv2+"\n---\n"+deploymentWebYAMLv1, ""). - AndRunInput("kubectl --context kubecontext apply -f -", deploymentAppYAMLv2). - AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f "+tmpDir.Path("deployment-app.yaml")+" -f "+tmpDir.Path("deployment-web.yaml"), deploymentAppYAML+"\n"+deploymentWebYAML). - AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", deploymentAppYAMLv2+"\n---\n"+deploymentWebYAMLv1, ""), + CmdRunOut("kubectl version --client -ojson", KubectlVersion112). + AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f "+tmpDir.Path("deployment-app.yaml")+" -f "+tmpDir.Path("deployment-web.yaml"), DeploymentAppYAML+"\n"+DeploymentWebYAML). + AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", DeploymentAppYAMLv1+"\n---\n"+DeploymentWebYAMLv1, ""). + AndRunInput("kubectl --context kubecontext apply -f -", DeploymentAppYAMLv1+"\n---\n"+DeploymentWebYAMLv1). + AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f "+tmpDir.Path("deployment-app.yaml")+" -f "+tmpDir.Path("deployment-web.yaml"), DeploymentAppYAML+"\n"+DeploymentWebYAML). + AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", DeploymentAppYAMLv2+"\n---\n"+DeploymentWebYAMLv1, ""). + AndRunInput("kubectl --context kubecontext apply -f -", DeploymentAppYAMLv2). + AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f "+tmpDir.Path("deployment-app.yaml")+" -f "+tmpDir.Path("deployment-web.yaml"), DeploymentAppYAML+"\n"+DeploymentWebYAML). + AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", DeploymentAppYAMLv2+"\n---\n"+DeploymentWebYAMLv1, ""), ) - deployer, err := NewKubectlDeployer(&kubectlConfig{ + deployer, err := NewDeployer(&kubectlConfig{ workingDir: ".", kubectl: latest.KubectlDeploy{ Manifests: []string{tmpDir.Path("deployment-app.yaml"), tmpDir.Path("deployment-web.yaml")}}, @@ -482,36 +429,36 @@ func TestKubectlRedeploy(t *testing.T) { func TestKubectlWaitForDeletions(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { - tmpDir := t.NewTempDir().Write("deployment-web.yaml", deploymentWebYAML) + tmpDir := t.NewTempDir().Write("deployment-web.yaml", DeploymentWebYAML) t.Override(&util.DefaultExecCommand, testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion112). - AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f "+tmpDir.Path("deployment-web.yaml"), deploymentWebYAML). - AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, `{ + CmdRunOut("kubectl version --client -ojson", KubectlVersion112). + AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f "+tmpDir.Path("deployment-web.yaml"), DeploymentWebYAML). + AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", DeploymentWebYAMLv1, `{ "items":[ {"metadata":{"deletionTimestamp":"2020-07-24T12:40:32Z","name":"leeroy-web"}}, {"metadata":{"deletionTimestamp":"2020-07-24T12:40:32Z","name":"leeroy-app"}}, {"metadata":{"name":"leeroy-front"}} ] }`). - AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, `{ + AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", DeploymentWebYAMLv1, `{ "items":[ {"metadata":{"deletionTimestamp":"2020-07-24T12:40:32Z","name":"leeroy-web"}}, {"metadata":{"deletionTimestamp":"2020-07-24T12:40:32Z","name":"leeroy-app"}}, {"metadata":{"name":"leeroy-front"}} ] }`). - AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, `{ + AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", DeploymentWebYAMLv1, `{ "items":[ {"metadata":{"deletionTimestamp":"2020-07-24T12:40:32Z","name":"leeroy-web"}}, {"metadata":{"name":"leeroy-front"}} ] }`). - AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, ""). - AndRunInput("kubectl --context kubecontext apply -f -", deploymentWebYAMLv1), + AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", DeploymentWebYAMLv1, ""). + AndRunInput("kubectl --context kubecontext apply -f -", DeploymentWebYAMLv1), ) - deployer, err := NewKubectlDeployer(&kubectlConfig{ + deployer, err := NewDeployer(&kubectlConfig{ workingDir: tmpDir.Root(), kubectl: latest.KubectlDeploy{ Manifests: []string{tmpDir.Path("deployment-web.yaml")}, @@ -538,12 +485,12 @@ func TestKubectlWaitForDeletions(t *testing.T) { func TestKubectlWaitForDeletionsFails(t *testing.T) { testutil.Run(t, "", func(t *testutil.T) { - tmpDir := t.NewTempDir().Write("deployment-web.yaml", deploymentWebYAML) + tmpDir := t.NewTempDir().Write("deployment-web.yaml", DeploymentWebYAML) t.Override(&util.DefaultExecCommand, testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion112). - AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f "+tmpDir.Path("deployment-web.yaml"), deploymentWebYAML). - AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, `{ + CmdRunOut("kubectl version --client -ojson", KubectlVersion112). + AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f "+tmpDir.Path("deployment-web.yaml"), DeploymentWebYAML). + AndRunInputOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", DeploymentWebYAMLv1, `{ "items":[ {"metadata":{"deletionTimestamp":"2020-07-24T12:40:32Z","name":"leeroy-web"}}, {"metadata":{"deletionTimestamp":"2020-07-24T12:40:32Z","name":"leeroy-app"}} @@ -551,7 +498,7 @@ func TestKubectlWaitForDeletionsFails(t *testing.T) { }`), ) - deployer, err := NewKubectlDeployer(&kubectlConfig{ + deployer, err := NewDeployer(&kubectlConfig{ workingDir: tmpDir.Root(), kubectl: latest.KubectlDeploy{ Manifests: []string{tmpDir.Path("deployment-web.yaml")}, @@ -622,7 +569,7 @@ func TestDependencies(t *testing.T) { Touch("00/b.yaml", "00/a.yaml"). Chdir() - k, err := NewKubectlDeployer(&kubectlConfig{ + k, err := NewDeployer(&kubectlConfig{ kubectl: latest.KubectlDeploy{ Manifests: test.manifests, }, @@ -737,9 +684,9 @@ spec: testutil.Run(t, test.description, func(t *testutil.T) { tmpDir := t.NewTempDir().Write("deployment.yaml", test.input) t.Override(&util.DefaultExecCommand, testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion112). + CmdRunOut("kubectl version --client -ojson", KubectlVersion112). AndRunOut("kubectl --context kubecontext create --dry-run -oyaml -f "+tmpDir.Path("deployment.yaml"), test.input)) - deployer, err := NewKubectlDeployer(&kubectlConfig{ + deployer, err := NewDeployer(&kubectlConfig{ workingDir: ".", defaultRepo: "gcr.io/project", kubectl: latest.KubectlDeploy{ @@ -769,26 +716,26 @@ func TestGCSManifests(t *testing.T) { Manifests: []string{"gs://dev/deployment.yaml"}, }, commands: testutil. - CmdRunOut(fmt.Sprintf("gsutil cp -r %s %s", "gs://dev/deployment.yaml", manifestTmpDir), "log"). - AndRunOut("kubectl version --client -ojson", kubectlVersion112). - AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f "+filepath.Join(manifestTmpDir, "deployment.yaml"), deploymentWebYAML). + CmdRunOut(fmt.Sprintf("gsutil cp -r %s %s", "gs://dev/deployment.yaml", manifest.ManifestTmpDir), "log"). + AndRunOut("kubectl version --client -ojson", KubectlVersion112). + AndRunOut("kubectl --context kubecontext --namespace testNamespace create --dry-run -oyaml -f "+filepath.Join(manifest.ManifestTmpDir, "deployment.yaml"), DeploymentWebYAML). AndRun("kubectl --context kubecontext --namespace testNamespace apply -f -"), skipRender: true, }} for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { t.Override(&util.DefaultExecCommand, test.commands) - if err := os.MkdirAll(manifestTmpDir, os.ModePerm); err != nil { + if err := os.MkdirAll(manifest.ManifestTmpDir, os.ModePerm); err != nil { t.Fatal(err) } - if err := ioutil.WriteFile(manifestTmpDir+"/deployment.yaml", []byte(deploymentWebYAML), os.ModePerm); err != nil { + if err := ioutil.WriteFile(manifest.ManifestTmpDir+"/deployment.yaml", []byte(DeploymentWebYAML), os.ModePerm); err != nil { t.Fatal(err) } - k, err := NewKubectlDeployer(&kubectlConfig{ + k, err := NewDeployer(&kubectlConfig{ workingDir: ".", kubectl: test.kubectl, skipRender: test.skipRender, - RunContext: runcontext.RunContext{Opts: config.SkaffoldOptions{Namespace: testNamespace}}, + RunContext: runcontext.RunContext{Opts: config.SkaffoldOptions{Namespace: TestNamespace}}, }, nil) t.RequireNoError(err) @@ -809,7 +756,7 @@ type kubectlConfig struct { kubectl latest.KubectlDeploy } -func (c *kubectlConfig) GetKubeContext() string { return testKubeContext } +func (c *kubectlConfig) GetKubeContext() string { return "kubecontext" } func (c *kubectlConfig) GetKubeNamespace() string { return c.Opts.Namespace } func (c *kubectlConfig) WorkingDir() string { return c.workingDir } func (c *kubectlConfig) SkipRender() bool { return c.skipRender } diff --git a/pkg/skaffold/deploy/kustomize.go b/pkg/skaffold/deploy/kustomize/kustomize.go similarity index 61% rename from pkg/skaffold/deploy/kustomize.go rename to pkg/skaffold/deploy/kustomize/kustomize.go index 4b2ae88c7c4..b5af0d9562f 100644 --- a/pkg/skaffold/deploy/kustomize.go +++ b/pkg/skaffold/deploy/kustomize/kustomize.go @@ -14,17 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deploy +package kustomize import ( "context" "fmt" "io" - "io/ioutil" "os" "os/exec" "path/filepath" - "strings" "github.com/segmentio/textio" yamlv3 "gopkg.in/yaml.v3" @@ -32,12 +30,13 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" - deploy "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" + deployutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/util" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/warnings" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/yaml" ) var ( @@ -90,17 +89,18 @@ type secretGenerator struct { Envs []string `yaml:"envs"` } -// KustomizeDeployer deploys workflows using kustomize CLI. -type KustomizeDeployer struct { +// Deployer deploys workflows using kustomize CLI. +type Deployer struct { *latest.KustomizeDeploy - kubectl deploy.CLI + + kubectl kubectl.CLI insecureRegistries map[string]bool labels map[string]string globalConfig string useKubectlKustomize bool } -func NewKustomizeDeployer(cfg Config, labels map[string]string) (*KustomizeDeployer, error) { +func NewDeployer(cfg kubectl.Config, labels map[string]string) (*Deployer, error) { defaultNamespace := "" if cfg.Pipeline().Deploy.KustomizeDeploy.DefaultNamespace != nil { var err error @@ -110,11 +110,11 @@ func NewKustomizeDeployer(cfg Config, labels map[string]string) (*KustomizeDeplo } } - kubectl := deploy.NewCLI(cfg, cfg.Pipeline().Deploy.KustomizeDeploy.Flags, defaultNamespace) + kubectl := kubectl.NewCLI(cfg, cfg.Pipeline().Deploy.KustomizeDeploy.Flags, defaultNamespace) // if user has kustomize binary, prioritize that over kubectl kustomize useKubectlKustomize := !kustomizeBinaryCheck() && kubectlVersionCheck(kubectl) - return &KustomizeDeployer{ + return &Deployer{ KustomizeDeploy: cfg.Pipeline().Deploy.KustomizeDeploy, kubectl: kubectl, insecureRegistries: cfg.GetInsecureRegistries(), @@ -132,7 +132,7 @@ func kustomizeBinaryExists() bool { } // Check that kubectl version is valid to use kubectl kustomize -func kubectlVersionCheck(kubectl deploy.CLI) bool { +func kubectlVersionCheck(kubectl kubectl.CLI) bool { gt, err := kubectl.CompareVersionTo(context.Background(), 1, 14) if err != nil { return false @@ -142,7 +142,7 @@ func kubectlVersionCheck(kubectl deploy.CLI) bool { } // Deploy runs `kubectl apply` on the manifest generated by kustomize. -func (k *KustomizeDeployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact) ([]string, error) { +func (k *Deployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact) ([]string, error) { manifests, err := k.renderManifests(ctx, out, builds) if err != nil { return nil, err @@ -169,7 +169,7 @@ func (k *KustomizeDeployer) Deploy(ctx context.Context, out io.Writer, builds [] return namespaces, nil } -func (k *KustomizeDeployer) renderManifests(ctx context.Context, out io.Writer, builds []build.Artifact) (deploy.ManifestList, error) { +func (k *Deployer) renderManifests(ctx context.Context, out io.Writer, builds []build.Artifact) (manifest.ManifestList, error) { if err := k.kubectl.CheckVersion(ctx); err != nil { color.Default.Fprintln(out, "kubectl client version:", k.kubectl.Version(ctx)) color.Default.Fprintln(out, err) @@ -194,18 +194,15 @@ func (k *KustomizeDeployer) renderManifests(ctx context.Context, out io.Writer, return nil, fmt.Errorf("replacing images in manifests: %w", err) } - for _, transform := range manifestTransforms { - manifests, err = transform(manifests, builds, Registries{k.insecureRegistries, debugHelpersRegistry}) - if err != nil { - return nil, fmt.Errorf("unable to transform manifests: %w", err) - } + if manifests, err = manifest.ApplyTransforms(manifests, builds, k.insecureRegistries, debugHelpersRegistry); err != nil { + return nil, err } return manifests.SetLabels(k.labels) } // Cleanup deletes what was deployed by calling Deploy. -func (k *KustomizeDeployer) Cleanup(ctx context.Context, out io.Writer) error { +func (k *Deployer) Cleanup(ctx context.Context, out io.Writer) error { manifests, err := k.readManifests(ctx) if err != nil { return fmt.Errorf("reading manifests: %w", err) @@ -219,24 +216,24 @@ func (k *KustomizeDeployer) Cleanup(ctx context.Context, out io.Writer) error { } // Dependencies lists all the files that describe what needs to be deployed. -func (k *KustomizeDeployer) Dependencies() ([]string, error) { - deps := newStringSet() +func (k *Deployer) Dependencies() ([]string, error) { + deps := deployutil.NewStringSet() for _, kustomizePath := range k.KustomizePaths { - depsForKustomization, err := dependenciesForKustomization(kustomizePath) + depsForKustomization, err := DependenciesForKustomization(kustomizePath) if err != nil { return nil, err } - deps.insert(depsForKustomization...) + deps.Insert(depsForKustomization...) } - return deps.toList(), nil + return deps.ToList(), nil } -func (k *KustomizeDeployer) Render(ctx context.Context, out io.Writer, builds []build.Artifact, offline bool, filepath string) error { +func (k *Deployer) Render(ctx context.Context, out io.Writer, builds []build.Artifact, offline bool, filepath string) error { manifests, err := k.renderManifests(ctx, out, builds) if err != nil { return err } - return outputRenderedManifests(manifests.String(), filepath, out) + return manifest.Write(manifests.String(), filepath, out) } // Values of `patchesStrategicMerge` can be either: @@ -267,102 +264,6 @@ func (p *patchWrapper) UnmarshalYAML(unmarshal func(interface{}) error) (err err return nil } -func dependenciesForKustomization(dir string) ([]string, error) { - var deps []string - - path, err := findKustomizationConfig(dir) - if err != nil { - // No kustomization config found so assume it's remote and stop traversing - return deps, nil - } - - buf, err := ioutil.ReadFile(path) - if err != nil { - return nil, err - } - - content := kustomization{} - if err := yaml.Unmarshal(buf, &content); err != nil { - return nil, err - } - - deps = append(deps, path) - - candidates := append(content.Bases, content.Resources...) - candidates = append(candidates, content.Components...) - - for _, candidate := range candidates { - // If the file doesn't exist locally, we can assume it's a remote file and - // skip it, since we can't monitor remote files. Kustomize itself will - // handle invalid/missing files. - local, mode := pathExistsLocally(candidate, dir) - if !local { - continue - } - - if mode.IsDir() { - candidateDeps, err := dependenciesForKustomization(filepath.Join(dir, candidate)) - if err != nil { - return nil, err - } - deps = append(deps, candidateDeps...) - } else { - deps = append(deps, filepath.Join(dir, candidate)) - } - } - - for _, patch := range content.PatchesStrategicMerge { - if patch.Path != "" { - deps = append(deps, filepath.Join(dir, patch.Path)) - } - } - - deps = append(deps, util.AbsolutePaths(dir, content.CRDs)...) - - for _, patch := range content.Patches { - if patch.Path != "" { - deps = append(deps, filepath.Join(dir, patch.Path)) - } - } - - for _, jsonPatch := range content.PatchesJSON6902 { - if jsonPatch.Path != "" { - deps = append(deps, filepath.Join(dir, jsonPatch.Path)) - } - } - - for _, generator := range content.ConfigMapGenerator { - deps = append(deps, util.AbsolutePaths(dir, generator.Files)...) - envs := generator.Envs - if generator.Env != "" { - envs = append(envs, generator.Env) - } - deps = append(deps, util.AbsolutePaths(dir, envs)...) - } - - for _, generator := range content.SecretGenerator { - deps = append(deps, util.AbsolutePaths(dir, generator.Files)...) - envs := generator.Envs - if generator.Env != "" { - envs = append(envs, generator.Env) - } - deps = append(deps, util.AbsolutePaths(dir, envs)...) - } - - return deps, nil -} - -// A Kustomization config must be at the root of the directory. Kustomize will -// error if more than one of these files exists so order doesn't matter. -func findKustomizationConfig(dir string) (string, error) { - for _, candidate := range kustomizeFilePaths { - if local, _ := pathExistsLocally(candidate, dir); local { - return filepath.Join(dir, candidate), nil - } - } - return "", fmt.Errorf("no Kustomization configuration found in directory: %s", dir) -} - func pathExistsLocally(filename string, workingDir string) (bool, os.FileMode) { path := filename if !filepath.IsAbs(filename) { @@ -374,16 +275,16 @@ func pathExistsLocally(filename string, workingDir string) (bool, os.FileMode) { return false, 0 } -func (k *KustomizeDeployer) readManifests(ctx context.Context) (deploy.ManifestList, error) { - var manifests deploy.ManifestList +func (k *Deployer) readManifests(ctx context.Context) (manifest.ManifestList, error) { + var manifests manifest.ManifestList for _, kustomizePath := range k.KustomizePaths { var out []byte var err error if k.useKubectlKustomize { - out, err = k.kubectl.Kustomize(ctx, buildCommandArgs(k.BuildArgs, kustomizePath)) + out, err = k.kubectl.Kustomize(ctx, BuildCommandArgs(k.BuildArgs, kustomizePath)) } else { - cmd := exec.CommandContext(ctx, "kustomize", append([]string{"build"}, buildCommandArgs(k.BuildArgs, kustomizePath)...)...) + cmd := exec.CommandContext(ctx, "kustomize", append([]string{"build"}, BuildCommandArgs(k.BuildArgs, kustomizePath)...)...) out, err = util.RunCmdOut(cmd) } @@ -399,23 +300,6 @@ func (k *KustomizeDeployer) readManifests(ctx context.Context) (deploy.ManifestL return manifests, nil } -func buildCommandArgs(buildArgs []string, kustomizePath string) []string { - var args []string - - if len(buildArgs) > 0 { - for _, v := range buildArgs { - parts := strings.Split(v, " ") - args = append(args, parts...) - } - } - - if len(kustomizePath) > 0 { - args = append(args, kustomizePath) - } - - return args -} - func IsKustomizationBase(path string) bool { return filepath.Dir(path) == basePath } diff --git a/pkg/skaffold/deploy/kustomize_test.go b/pkg/skaffold/deploy/kustomize/kustomize_test.go similarity index 89% rename from pkg/skaffold/deploy/kustomize_test.go rename to pkg/skaffold/deploy/kustomize/kustomize_test.go index 9f60588bf6e..cbe9db7c191 100644 --- a/pkg/skaffold/deploy/kustomize_test.go +++ b/pkg/skaffold/deploy/kustomize/kustomize_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deploy +package kustomize import ( "bytes" @@ -27,6 +27,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" @@ -51,7 +52,7 @@ func TestKustomizeDeploy(t *testing.T) { KustomizePaths: []string{"."}, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion118). + CmdRunOut("kubectl version --client -ojson", kubectl.KubectlVersion118). AndRunOut("kustomize build .", ""), kustomizeCmdPresent: true, }, @@ -61,9 +62,9 @@ func TestKustomizeDeploy(t *testing.T) { KustomizePaths: []string{"."}, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion118). - AndRunOut("kustomize build .", deploymentWebYAML). - AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, ""). + CmdRunOut("kubectl version --client -ojson", kubectl.KubectlVersion118). + AndRunOut("kustomize build .", kubectl.DeploymentWebYAML). + AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", kubectl.DeploymentWebYAMLv1, ""). AndRun("kubectl --context kubecontext --namespace testNamespace apply -f - --force --grace-period=0"), builds: []build.Artifact{{ ImageName: "leeroy-web", @@ -76,12 +77,12 @@ func TestKustomizeDeploy(t *testing.T) { description: "deploy success (default namespace)", kustomize: latest.KustomizeDeploy{ KustomizePaths: []string{"."}, - DefaultNamespace: &testNamespace2, + DefaultNamespace: &kubectl.TestNamespace2, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion112). - AndRunOut("kustomize build .", deploymentWebYAML). - AndRunInputOut("kubectl --context kubecontext --namespace testNamespace2 get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, ""). + CmdRunOut("kubectl version --client -ojson", kubectl.KubectlVersion112). + AndRunOut("kustomize build .", kubectl.DeploymentWebYAML). + AndRunInputOut("kubectl --context kubecontext --namespace testNamespace2 get -f - --ignore-not-found -ojson", kubectl.DeploymentWebYAMLv1, ""). AndRun("kubectl --context kubecontext --namespace testNamespace2 apply -f - --force --grace-period=0"), builds: []build.Artifact{{ ImageName: "leeroy-web", @@ -95,12 +96,12 @@ func TestKustomizeDeploy(t *testing.T) { description: "deploy success (default namespace with env template)", kustomize: latest.KustomizeDeploy{ KustomizePaths: []string{"."}, - DefaultNamespace: &testNamespace2FromEnvTemplate, + DefaultNamespace: &kubectl.TestNamespace2FromEnvTemplate, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion112). - AndRunOut("kustomize build .", deploymentWebYAML). - AndRunInputOut("kubectl --context kubecontext --namespace testNamespace2 get -f - --ignore-not-found -ojson", deploymentWebYAMLv1, ""). + CmdRunOut("kubectl version --client -ojson", kubectl.KubectlVersion112). + AndRunOut("kustomize build .", kubectl.DeploymentWebYAML). + AndRunInputOut("kubectl --context kubecontext --namespace testNamespace2 get -f - --ignore-not-found -ojson", kubectl.DeploymentWebYAMLv1, ""). AndRun("kubectl --context kubecontext --namespace testNamespace2 apply -f - --force --grace-period=0"), builds: []build.Artifact{{ ImageName: "leeroy-web", @@ -119,10 +120,10 @@ func TestKustomizeDeploy(t *testing.T) { KustomizePaths: []string{"a", "b"}, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion118). - AndRunOut("kustomize build a", deploymentWebYAML). - AndRunOut("kustomize build b", deploymentAppYAML). - AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentWebYAMLv1+"\n---\n"+deploymentAppYAMLv1, ""). + CmdRunOut("kubectl version --client -ojson", kubectl.KubectlVersion118). + AndRunOut("kustomize build a", kubectl.DeploymentWebYAML). + AndRunOut("kustomize build b", kubectl.DeploymentAppYAML). + AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", kubectl.DeploymentWebYAMLv1+"\n---\n"+kubectl.DeploymentAppYAMLv1, ""). AndRun("kubectl --context kubecontext --namespace testNamespace apply -f - --force --grace-period=0"), builds: []build.Artifact{ { @@ -143,10 +144,10 @@ func TestKustomizeDeploy(t *testing.T) { KustomizePaths: []string{"a", "b"}, }, commands: testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion118). - AndRunOut("kubectl --context kubecontext --namespace testNamespace kustomize a", deploymentWebYAML). - AndRunOut("kubectl --context kubecontext --namespace testNamespace kustomize b", deploymentAppYAML). - AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", deploymentWebYAMLv1+"\n---\n"+deploymentAppYAMLv1, ""). + CmdRunOut("kubectl version --client -ojson", kubectl.KubectlVersion118). + AndRunOut("kubectl --context kubecontext --namespace testNamespace kustomize a", kubectl.DeploymentWebYAML). + AndRunOut("kubectl --context kubecontext --namespace testNamespace kustomize b", kubectl.DeploymentAppYAML). + AndRunInputOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", kubectl.DeploymentWebYAMLv1+"\n---\n"+kubectl.DeploymentAppYAMLv1, ""). AndRun("kubectl --context kubecontext --namespace testNamespace apply -f - --force --grace-period=0"), builds: []build.Artifact{ { @@ -172,10 +173,10 @@ func TestKustomizeDeploy(t *testing.T) { skaffoldNamespaceOption := "" if !test.skipSkaffoldNamespaceOption { - skaffoldNamespaceOption = testNamespace + skaffoldNamespaceOption = kubectl.TestNamespace } - k, err := NewKustomizeDeployer(&kustomizeConfig{ + k, err := NewDeployer(&kustomizeConfig{ workingDir: ".", force: test.forceDeploy, waitForDeletions: config.WaitForDeletions{ @@ -210,7 +211,7 @@ func TestKustomizeCleanup(t *testing.T) { KustomizePaths: []string{tmpDir.Root()}, }, commands: testutil. - CmdRunOut("kustomize build "+tmpDir.Root(), deploymentWebYAML). + CmdRunOut("kustomize build "+tmpDir.Root(), kubectl.DeploymentWebYAML). AndRun("kubectl --context kubecontext --namespace testNamespace delete --ignore-not-found=true -f -"), }, { @@ -219,8 +220,8 @@ func TestKustomizeCleanup(t *testing.T) { KustomizePaths: tmpDir.Paths("a", "b"), }, commands: testutil. - CmdRunOut("kustomize build "+tmpDir.Path("a"), deploymentWebYAML). - AndRunOut("kustomize build "+tmpDir.Path("b"), deploymentAppYAML). + CmdRunOut("kustomize build "+tmpDir.Path("a"), kubectl.DeploymentWebYAML). + AndRunOut("kustomize build "+tmpDir.Path("b"), kubectl.DeploymentAppYAML). AndRun("kubectl --context kubecontext --namespace testNamespace delete --ignore-not-found=true -f -"), }, { @@ -229,7 +230,7 @@ func TestKustomizeCleanup(t *testing.T) { KustomizePaths: []string{tmpDir.Root()}, }, commands: testutil. - CmdRunOut("kustomize build "+tmpDir.Root(), deploymentWebYAML). + CmdRunOut("kustomize build "+tmpDir.Root(), kubectl.DeploymentWebYAML). AndRunErr("kubectl --context kubecontext --namespace testNamespace delete --ignore-not-found=true -f -", errors.New("BUG")), shouldErr: true, }, @@ -248,11 +249,11 @@ func TestKustomizeCleanup(t *testing.T) { t.Override(&util.DefaultExecCommand, test.commands) t.Override(&kustomizeBinaryCheck, func() bool { return true }) - k, err := NewKustomizeDeployer(&kustomizeConfig{ + k, err := NewDeployer(&kustomizeConfig{ workingDir: tmpDir.Root(), kustomize: test.kustomize, RunContext: runcontext.RunContext{Opts: config.SkaffoldOptions{ - Namespace: testNamespace}}, + Namespace: kubectl.TestNamespace}}, }, nil) t.RequireNoError(err) err = k.Cleanup(context.Background(), ioutil.Discard) @@ -455,7 +456,7 @@ func TestDependenciesForKustomization(t *testing.T) { tmpDir.Write(path, contents) } - k, err := NewKustomizeDeployer(&kustomizeConfig{ + k, err := NewDeployer(&kustomizeConfig{ kustomize: latest.KustomizeDeploy{ KustomizePaths: kustomizePaths, }, @@ -528,7 +529,7 @@ func TestKustomizeBuildCommandArgs(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { - args := buildCommandArgs(test.buildArgs, test.kustomizePath) + args := BuildCommandArgs(test.buildArgs, test.kustomizePath) t.CheckDeepEqual(test.expectedArgs, args) }) } @@ -692,7 +693,7 @@ spec: testutil.Run(t, test.description, func(t *testutil.T) { var kustomizationPaths []string fakeCmd := testutil. - CmdRunOut("kubectl version --client -ojson", kubectlVersion112) + CmdRunOut("kubectl version --client -ojson", kubectl.KubectlVersion112) for _, kustomizationCall := range test.kustomizations { fakeCmd.AndRunOut("kustomize build "+kustomizationCall.folder, kustomizationCall.buildResult) kustomizationPaths = append(kustomizationPaths, kustomizationCall.folder) @@ -700,12 +701,12 @@ spec: t.Override(&util.DefaultExecCommand, fakeCmd) t.NewTempDir().Chdir() - k, err := NewKustomizeDeployer(&kustomizeConfig{ + k, err := NewDeployer(&kustomizeConfig{ workingDir: ".", kustomize: latest.KustomizeDeploy{ KustomizePaths: kustomizationPaths, }, - RunContext: runcontext.RunContext{Opts: config.SkaffoldOptions{Namespace: testNamespace}}, + RunContext: runcontext.RunContext{Opts: config.SkaffoldOptions{Namespace: kubectl.TestNamespace}}, }, test.labels) t.RequireNoError(err) @@ -728,7 +729,7 @@ type kustomizeConfig struct { func (c *kustomizeConfig) ForceDeploy() bool { return c.force } func (c *kustomizeConfig) WaitForDeletions() config.WaitForDeletions { return c.waitForDeletions } func (c *kustomizeConfig) WorkingDir() string { return c.workingDir } -func (c *kustomizeConfig) GetKubeContext() string { return testKubeContext } +func (c *kustomizeConfig) GetKubeContext() string { return kubectl.TestKubeContext } func (c *kustomizeConfig) GetKubeNamespace() string { return c.Opts.Namespace } func (c *kustomizeConfig) Pipeline() latest.Pipeline { var pipeline latest.Pipeline diff --git a/pkg/skaffold/deploy/kustomize/util.go b/pkg/skaffold/deploy/kustomize/util.go new file mode 100644 index 00000000000..1648897f625 --- /dev/null +++ b/pkg/skaffold/deploy/kustomize/util.go @@ -0,0 +1,145 @@ +/* +Copyright 2020 The Skaffold Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kustomize + +import ( + "fmt" + "io/ioutil" + "path/filepath" + "strings" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/yaml" +) + +// DependenciesForKustomization finds common kustomize artifacts relative to the +// provided working dir, and collects them into a list of files to be passed +// to the file watcher. +func DependenciesForKustomization(dir string) ([]string, error) { + var deps []string + + path, err := FindKustomizationConfig(dir) + if err != nil { + // No kustomization config found so assume it's remote and stop traversing + return deps, nil + } + + buf, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + content := kustomization{} + if err := yaml.Unmarshal(buf, &content); err != nil { + return nil, err + } + + deps = append(deps, path) + + candidates := append(content.Bases, content.Resources...) + candidates = append(candidates, content.Components...) + + for _, candidate := range candidates { + // If the file doesn't exist locally, we can assume it's a remote file and + // skip it, since we can't monitor remote files. Kustomize itself will + // handle invalid/missing files. + local, mode := pathExistsLocally(candidate, dir) + if !local { + continue + } + + if mode.IsDir() { + candidateDeps, err := DependenciesForKustomization(filepath.Join(dir, candidate)) + if err != nil { + return nil, err + } + deps = append(deps, candidateDeps...) + } else { + deps = append(deps, filepath.Join(dir, candidate)) + } + } + + for _, patch := range content.PatchesStrategicMerge { + if patch.Path != "" { + deps = append(deps, filepath.Join(dir, patch.Path)) + } + } + + deps = append(deps, util.AbsolutePaths(dir, content.CRDs)...) + + for _, patch := range content.Patches { + if patch.Path != "" { + deps = append(deps, filepath.Join(dir, patch.Path)) + } + } + + for _, jsonPatch := range content.PatchesJSON6902 { + if jsonPatch.Path != "" { + deps = append(deps, filepath.Join(dir, jsonPatch.Path)) + } + } + + for _, generator := range content.ConfigMapGenerator { + deps = append(deps, util.AbsolutePaths(dir, generator.Files)...) + envs := generator.Envs + if generator.Env != "" { + envs = append(envs, generator.Env) + } + deps = append(deps, util.AbsolutePaths(dir, envs)...) + } + + for _, generator := range content.SecretGenerator { + deps = append(deps, util.AbsolutePaths(dir, generator.Files)...) + envs := generator.Envs + if generator.Env != "" { + envs = append(envs, generator.Env) + } + deps = append(deps, util.AbsolutePaths(dir, envs)...) + } + + return deps, nil +} + +// FindKustomizationConfig finds the kustomization config relative to the provided dir. +// A Kustomization config must be at the root of the directory. Kustomize will +// error if more than one of these files exists so order doesn't matter. +func FindKustomizationConfig(dir string) (string, error) { + for _, candidate := range kustomizeFilePaths { + if local, _ := pathExistsLocally(candidate, dir); local { + return filepath.Join(dir, candidate), nil + } + } + return "", fmt.Errorf("no Kustomization configuration found in directory: %s", dir) +} + +// BuildCommandArgs returns a list of build args to be passed to kustomize. +func BuildCommandArgs(buildArgs []string, kustomizePath string) []string { + var args []string + + if len(buildArgs) > 0 { + for _, v := range buildArgs { + parts := strings.Split(v, " ") + args = append(args, parts...) + } + } + + if len(kustomizePath) > 0 { + args = append(args, kustomizePath) + } + + return args +} diff --git a/pkg/skaffold/deploy/labeller.go b/pkg/skaffold/deploy/label/labeller.go similarity index 95% rename from pkg/skaffold/deploy/labeller.go rename to pkg/skaffold/deploy/label/labeller.go index a242d430225..5d6e8fd909d 100644 --- a/pkg/skaffold/deploy/labeller.go +++ b/pkg/skaffold/deploy/label/labeller.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deploy +package label import ( "fmt" @@ -68,3 +68,7 @@ func (d *DefaultLabeller) Labels() map[string]string { func (d *DefaultLabeller) RunIDSelector() string { return fmt.Sprintf("%s=%s", RunIDLabel, d.Labels()[RunIDLabel]) } + +func (d *DefaultLabeller) GetRunID() string { + return d.runID +} diff --git a/pkg/skaffold/deploy/labels.go b/pkg/skaffold/deploy/label/labels.go similarity index 93% rename from pkg/skaffold/deploy/labels.go rename to pkg/skaffold/deploy/label/labels.go index d29f73d6db7..5b44de7d562 100644 --- a/pkg/skaffold/deploy/labels.go +++ b/pkg/skaffold/deploy/label/labels.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deploy +package label import ( "encoding/json" @@ -24,30 +24,25 @@ import ( "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" patch "k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/client-go/discovery" "k8s.io/client-go/dynamic" + deploy "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/types" kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" kubectx "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/context" ) -// Artifact contains all information about a completed deployment -type Artifact struct { - Obj runtime.Object - Namespace string -} - // retry 3 times to give the object time to propagate to the API server const ( tries = 3 sleeptime = 300 * time.Millisecond ) -func labelDeployResults(labels map[string]string, results []Artifact) error { +// Apply applies all provided labels to the created Kubernetes resources +func Apply(labels map[string]string, results []deploy.Artifact) error { if len(labels) == 0 { return nil } @@ -88,7 +83,7 @@ func addLabels(labels map[string]string, accessor metav1.Object) { accessor.SetLabels(kv) } -func updateRuntimeObject(client dynamic.Interface, disco discovery.DiscoveryInterface, labels map[string]string, res Artifact) error { +func updateRuntimeObject(client dynamic.Interface, disco discovery.DiscoveryInterface, labels map[string]string, res deploy.Artifact) error { originalJSON, _ := json.Marshal(res.Obj) modifiedObj := res.Obj.DeepCopyObject() accessor, err := meta.Accessor(modifiedObj) diff --git a/pkg/skaffold/deploy/labels_test.go b/pkg/skaffold/deploy/label/labels_test.go similarity index 94% rename from pkg/skaffold/deploy/labels_test.go rename to pkg/skaffold/deploy/label/labels_test.go index efe8927efd8..2d3b9125cfa 100644 --- a/pkg/skaffold/deploy/labels_test.go +++ b/pkg/skaffold/deploy/label/labels_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deploy +package label import ( "testing" @@ -28,6 +28,7 @@ import ( fakeclient "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/scheme" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/types" kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/testutil" ) @@ -44,7 +45,7 @@ func mockDynamicClient(m dynamic.Interface) func() (dynamic.Interface, error) { } } -func TestLabelDeployResults(t *testing.T) { +func TestApplyLabels(t *testing.T) { tests := []struct { description string existingLabels map[string]string @@ -106,7 +107,7 @@ func TestLabelDeployResults(t *testing.T) { t.Override(&kubernetesclient.DynamicClient, mockDynamicClient(dynClient)) // Patch labels - labelDeployResults(test.appliedLabels, []Artifact{{Obj: dep}}) + Apply(test.appliedLabels, []types.Artifact{{Obj: dep}}) // Check modified value modified, err := dynClient.Resource(schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}).Get("foo", metav1.GetOptions{}) diff --git a/pkg/skaffold/deploy/status_check.go b/pkg/skaffold/deploy/status/status_check.go similarity index 93% rename from pkg/skaffold/deploy/status_check.go rename to pkg/skaffold/deploy/status/status_check.go index 93913774bdb..6e096404a28 100644 --- a/pkg/skaffold/deploy/status_check.go +++ b/pkg/skaffold/deploy/status/status_check.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deploy +package status import ( "context" @@ -33,6 +33,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/diag/validator" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/label" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/resource" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" pkgkubectl "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" @@ -62,7 +63,7 @@ type counter struct { failed int32 } -type StatusCheckConfig interface { +type Config interface { kubectl.Config GetNamespaces() []string @@ -70,21 +71,21 @@ type StatusCheckConfig interface { Muted() config.Muted } -// StatusChecker wait for the application to be totally deployed. -type StatusChecker interface { +// Checker waits for the application to be totally deployed. +type Checker interface { Check(context.Context, io.Writer) error } -// StatusChecker runs status checks for pods and deployments +// statusChecker runs status checks for pods and deployments type statusChecker struct { - cfg StatusCheckConfig - labeller *DefaultLabeller + cfg Config + labeller *label.DefaultLabeller deadlineSeconds int muteLogs bool } // NewStatusChecker returns a status checker which runs checks on deployments and pods. -func NewStatusChecker(cfg StatusCheckConfig, labeller *DefaultLabeller) StatusChecker { +func NewStatusChecker(cfg Config, labeller *label.DefaultLabeller) Checker { return statusChecker{ muteLogs: cfg.Muted().MuteStatusCheck(), cfg: cfg, @@ -150,7 +151,7 @@ func (s statusChecker) statusCheck(ctx context.Context, out io.Writer) (proto.St return getSkaffoldDeployStatus(c, deployments) } -func getDeployments(client kubernetes.Interface, ns string, l *DefaultLabeller, deadlineDuration time.Duration) ([]*resource.Deployment, error) { +func getDeployments(client kubernetes.Interface, ns string, l *label.DefaultLabeller, deadlineDuration time.Duration) ([]*resource.Deployment, error) { deps, err := client.AppsV1().Deployments(ns).List(metav1.ListOptions{ LabelSelector: l.RunIDSelector(), }) @@ -167,7 +168,7 @@ func getDeployments(client kubernetes.Interface, ns string, l *DefaultLabeller, deadline = time.Duration(*d.Spec.ProgressDeadlineSeconds) * time.Second } pd := diag.New([]string{d.Namespace}). - WithLabel(RunIDLabel, l.Labels()[RunIDLabel]). + WithLabel(label.RunIDLabel, l.Labels()[label.RunIDLabel]). WithValidators([]validator.Validator{validator.NewPodValidator(client)}) for k, v := range d.Spec.Template.Labels { diff --git a/pkg/skaffold/deploy/status_check_test.go b/pkg/skaffold/deploy/status/status_check_test.go similarity index 95% rename from pkg/skaffold/deploy/status_check_test.go rename to pkg/skaffold/deploy/status/status_check_test.go index 0fd8172253e..7ade4e0cb7f 100644 --- a/pkg/skaffold/deploy/status_check_test.go +++ b/pkg/skaffold/deploy/status/status_check_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deploy +package status import ( "bytes" @@ -33,6 +33,8 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/diag" "github.com/GoogleContainerTools/skaffold/pkg/diag/validator" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/label" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/resource" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" @@ -43,7 +45,7 @@ import ( ) func TestGetDeployments(t *testing.T) { - labeller := NewLabeller(true, nil) + labeller := label.NewLabeller(true, nil) tests := []struct { description string deps []*appsv1.Deployment @@ -58,8 +60,8 @@ func TestGetDeployments(t *testing.T) { Name: "dep1", Namespace: "test", Labels: map[string]string{ - RunIDLabel: labeller.runID, - "random": "foo", + label.RunIDLabel: labeller.GetRunID(), + "random": "foo", }, }, Spec: appsv1.DeploymentSpec{ProgressDeadlineSeconds: utilpointer.Int32Ptr(10)}, @@ -69,7 +71,7 @@ func TestGetDeployments(t *testing.T) { Name: "dep2", Namespace: "test", Labels: map[string]string{ - RunIDLabel: labeller.runID, + label.RunIDLabel: labeller.GetRunID(), }, }, Spec: appsv1.DeploymentSpec{ProgressDeadlineSeconds: utilpointer.Int32Ptr(20)}, @@ -88,8 +90,8 @@ func TestGetDeployments(t *testing.T) { Name: "dep1", Namespace: "test", Labels: map[string]string{ - RunIDLabel: labeller.runID, - "random": "foo", + label.RunIDLabel: labeller.GetRunID(), + "random": "foo", }, }, Spec: appsv1.DeploymentSpec{ProgressDeadlineSeconds: utilpointer.Int32Ptr(300)}, @@ -107,7 +109,7 @@ func TestGetDeployments(t *testing.T) { Name: "dep1", Namespace: "test", Labels: map[string]string{ - RunIDLabel: labeller.runID, + label.RunIDLabel: labeller.GetRunID(), }, }, Spec: appsv1.DeploymentSpec{ProgressDeadlineSeconds: utilpointer.Int32Ptr(100)}, @@ -117,7 +119,7 @@ func TestGetDeployments(t *testing.T) { Name: "dep2", Namespace: "test", Labels: map[string]string{ - RunIDLabel: labeller.runID, + label.RunIDLabel: labeller.GetRunID(), }, }, }, @@ -135,7 +137,7 @@ func TestGetDeployments(t *testing.T) { Name: "dep1", Namespace: "test", Labels: map[string]string{ - RunIDLabel: labeller.runID, + label.RunIDLabel: labeller.GetRunID(), }, }, Spec: appsv1.DeploymentSpec{ProgressDeadlineSeconds: utilpointer.Int32Ptr(600)}, @@ -157,7 +159,7 @@ func TestGetDeployments(t *testing.T) { Name: "dep1", Namespace: "test", Labels: map[string]string{ - RunIDLabel: labeller.runID, + label.RunIDLabel: labeller.GetRunID(), }, }, Spec: appsv1.DeploymentSpec{ProgressDeadlineSeconds: utilpointer.Int32Ptr(100)}, @@ -167,7 +169,7 @@ func TestGetDeployments(t *testing.T) { Name: "dep2", Namespace: "test1", Labels: map[string]string{ - RunIDLabel: labeller.runID, + label.RunIDLabel: labeller.GetRunID(), }, }, Spec: appsv1.DeploymentSpec{ProgressDeadlineSeconds: utilpointer.Int32Ptr(100)}, @@ -201,7 +203,7 @@ func TestGetDeployments(t *testing.T) { Name: "dep1", Namespace: "test", Labels: map[string]string{ - RunIDLabel: "9876-6789", + label.RunIDLabel: "9876-6789", }, }, Spec: appsv1.DeploymentSpec{ProgressDeadlineSeconds: utilpointer.Int32Ptr(100)}, @@ -303,7 +305,7 @@ func TestGetDeployStatus(t *testing.T) { } func TestPrintSummaryStatus(t *testing.T) { - labeller := NewLabeller(true, nil) + labeller := label.NewLabeller(true, nil) tests := []struct { description string namespace string @@ -380,7 +382,7 @@ func TestPrintSummaryStatus(t *testing.T) { } func TestPrintStatus(t *testing.T) { - labeller := NewLabeller(true, nil) + labeller := label.NewLabeller(true, nil) tests := []struct { description string rs []*resource.Deployment @@ -625,4 +627,4 @@ type statusConfig struct { runcontext.RunContext // Embedded to provide the default values. } -func (c *statusConfig) GetKubeContext() string { return testKubeContext } +func (c *statusConfig) GetKubeContext() string { return kubectl.TestKubeContext } diff --git a/pkg/skaffold/deploy/transformations.go b/pkg/skaffold/deploy/transformations.go deleted file mode 100644 index b70589a2e40..00000000000 --- a/pkg/skaffold/deploy/transformations.go +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright 2019 The Skaffold Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package deploy - -import ( - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" -) - -type Registries struct { - InsecureRegistries map[string]bool - DebugHelpersRegistry string -} - -type ManifestTransform func(l kubectl.ManifestList, builds []build.Artifact, registries Registries) (kubectl.ManifestList, error) - -// Transforms are applied to manifests -var manifestTransforms []ManifestTransform - -// AddManifestTransform adds a transform to be applied when deploying. -func AddManifestTransform(newTransform ManifestTransform) { - manifestTransforms = append(manifestTransforms, newTransform) -} diff --git a/pkg/skaffold/deploy/types/types.go b/pkg/skaffold/deploy/types/types.go new file mode 100644 index 00000000000..98b681aaa65 --- /dev/null +++ b/pkg/skaffold/deploy/types/types.go @@ -0,0 +1,40 @@ +/* +Copyright 2020 The Skaffold Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package types + +import ( + "k8s.io/apimachinery/pkg/runtime" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" +) + +type Config interface { + docker.Config + + Pipeline() latest.Pipeline + GetWorkingDir() string + GlobalConfig() string + DefaultRepo() *string + SkipRender() bool +} + +// Artifact contains all information about a completed deployment +type Artifact struct { + Obj runtime.Object + Namespace string +} diff --git a/pkg/skaffold/deploy/util.go b/pkg/skaffold/deploy/util.go deleted file mode 100644 index db78d03e198..00000000000 --- a/pkg/skaffold/deploy/util.go +++ /dev/null @@ -1,172 +0,0 @@ -/* -Copyright 2019 The Skaffold Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package deploy - -import ( - "bufio" - "bytes" - "context" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - - "github.com/sirupsen/logrus" - k8syaml "k8s.io/apimachinery/pkg/util/yaml" - "k8s.io/client-go/kubernetes/scheme" - - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" -) - -const ( - manifestsStagingFolder = "manifest_tmp" - renderedManifestsStagingFile = "rendered_manifest.yaml" - gcsPrefix = "gs://" -) - -var manifestTmpDir = filepath.Join(os.TempDir(), manifestsStagingFolder) - -func parseRuntimeObject(namespace string, b []byte) (*Artifact, error) { - d := scheme.Codecs.UniversalDeserializer() - obj, _, err := d.Decode(b, nil, nil) - if err != nil { - return nil, fmt.Errorf("error decoding parsed yaml: %s", err.Error()) - } - return &Artifact{ - Obj: obj, - Namespace: namespace, - }, nil -} - -func parseReleaseInfo(namespace string, b *bufio.Reader) []Artifact { - var results []Artifact - - r := k8syaml.NewYAMLReader(b) - for i := 0; ; i++ { - doc, err := r.Read() - if err == io.EOF { - break - } - if err != nil { - logrus.Infof("error parsing object from string: %s", err.Error()) - continue - } - objNamespace, err := getObjectNamespaceIfDefined(doc, namespace) - if err != nil { - logrus.Infof("error parsing object from string: %s", err.Error()) - continue - } - obj, err := parseRuntimeObject(objNamespace, doc) - if err != nil { - if i > 0 { - logrus.Infof(err.Error()) - } - } else { - results = append(results, *obj) - logrus.Debugf("found deployed object: %+v", obj.Obj) - } - } - - return results -} - -func getObjectNamespaceIfDefined(doc []byte, ns string) (string, error) { - if i := bytes.Index(doc, []byte("apiVersion")); i >= 0 { - manifests := kubectl.ManifestList{doc[i:]} - namespaces, err := manifests.CollectNamespaces() - if err != nil { - return ns, err - } - if len(namespaces) > 0 { - return namespaces[0], nil - } - } - return ns, nil -} - -// Outputs rendered manifests to a file, a writer or a GCS bucket. -func outputRenderedManifests(renderedManifests string, output string, manifestOut io.Writer) error { - switch { - case output == "": - _, err := fmt.Fprintln(manifestOut, renderedManifests) - return err - case strings.HasPrefix(output, gcsPrefix): - tempDir, err := ioutil.TempDir("", manifestsStagingFolder) - if err != nil { - return fmt.Errorf("failed to create the tmp directory: %w", err) - } - defer os.RemoveAll(tempDir) - tempFile := filepath.Join(tempDir, renderedManifestsStagingFile) - if err := dumpToFile(renderedManifests, tempFile); err != nil { - return err - } - gcs := util.Gsutil{} - if err := gcs.Copy(context.Background(), tempFile, output, false); err != nil { - return fmt.Errorf("failed to copy rendered manifests to GCS: %w", err) - } - return nil - default: - return dumpToFile(renderedManifests, output) - } -} - -func dumpToFile(renderedManifests string, filepath string) error { - f, err := os.Create(filepath) - if err != nil { - return fmt.Errorf("opening file for writing manifests: %w", err) - } - defer f.Close() - _, err = f.WriteString(renderedManifests + "\n") - return err -} - -// Returns relative path pointing to the GCS temp dir -func downloadManifestsFromGCS(manifests []string) (string, error) { - if err := os.MkdirAll(manifestTmpDir, os.ModePerm); err != nil { - return "", fmt.Errorf("failed to create the tmp directory: %w", err) - } - for _, manifest := range manifests { - if manifest == "" || !strings.HasPrefix(manifest, gcsPrefix) { - return "", fmt.Errorf("%v is not a valid GCS path", manifest) - } - gcs := util.Gsutil{} - if err := gcs.Copy(context.Background(), manifest, manifestTmpDir, true); err != nil { - return "", fmt.Errorf("failed to download manifests fom GCS: %w", err) - } - } - return manifestTmpDir, nil -} - -// ApplyDefaultRepo applies the default repo to a given image tag. -func ApplyDefaultRepo(globalConfig string, defaultRepo *string, tag string) (string, error) { - repo, err := config.GetDefaultRepo(globalConfig, defaultRepo) - if err != nil { - return "", fmt.Errorf("getting default repo: %w", err) - } - - newTag, err := docker.SubstituteDefaultRepoIntoImage(repo, tag) - if err != nil { - return "", fmt.Errorf("applying default repo to %q: %w", tag, err) - } - - return newTag, nil -} diff --git a/pkg/skaffold/deploy/logfile.go b/pkg/skaffold/deploy/util/logfile.go similarity index 99% rename from pkg/skaffold/deploy/logfile.go rename to pkg/skaffold/deploy/util/logfile.go index 5013c12ffd0..d835a1af885 100644 --- a/pkg/skaffold/deploy/logfile.go +++ b/pkg/skaffold/deploy/util/logfile.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deploy +package util import ( "bytes" diff --git a/pkg/skaffold/deploy/logfile_test.go b/pkg/skaffold/deploy/util/logfile_test.go similarity index 99% rename from pkg/skaffold/deploy/logfile_test.go rename to pkg/skaffold/deploy/util/logfile_test.go index 9af6c90321b..04407e891bf 100644 --- a/pkg/skaffold/deploy/logfile_test.go +++ b/pkg/skaffold/deploy/util/logfile_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package deploy +package util import ( "bytes" diff --git a/pkg/skaffold/deploy/util/stringset.go b/pkg/skaffold/deploy/util/stringset.go new file mode 100644 index 00000000000..302259a13b8 --- /dev/null +++ b/pkg/skaffold/deploy/util/stringset.go @@ -0,0 +1,46 @@ +/* +Copyright 2020 The Skaffold Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import "sort" + +type unit struct{} + +// StringSet helps to de-duplicate a set of strings. +type StringSet map[string]unit + +// NewStringSet returns a new StringSet object. +func NewStringSet() StringSet { + return make(map[string]unit) +} + +// Insert adds strings to the set. +func (s StringSet) Insert(strings ...string) { + for _, item := range strings { + s[item] = unit{} + } +} + +// ToList returns the sorted list of inserted strings. +func (s StringSet) ToList() []string { + var res []string + for item := range s { + res = append(res, item) + } + sort.Strings(res) + return res +} diff --git a/pkg/skaffold/deploy/util/util.go b/pkg/skaffold/deploy/util/util.go new file mode 100644 index 00000000000..54b2c8372d1 --- /dev/null +++ b/pkg/skaffold/deploy/util/util.go @@ -0,0 +1,39 @@ +/* +Copyright 2019 The Skaffold Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "fmt" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" +) + +// ApplyDefaultRepo applies the default repo to a given image tag. +func ApplyDefaultRepo(globalConfig string, defaultRepo *string, tag string) (string, error) { + repo, err := config.GetDefaultRepo(globalConfig, defaultRepo) + if err != nil { + return "", fmt.Errorf("getting default repo: %w", err) + } + + newTag, err := docker.SubstituteDefaultRepoIntoImage(repo, tag) + if err != nil { + return "", fmt.Errorf("applying default repo to %q: %w", tag, err) + } + + return newTag, nil +} diff --git a/pkg/skaffold/initializer/analyze/helm.go b/pkg/skaffold/initializer/analyze/helm.go index 290a271252b..1b6ee6962c3 100644 --- a/pkg/skaffold/initializer/analyze/helm.go +++ b/pkg/skaffold/initializer/analyze/helm.go @@ -17,7 +17,7 @@ limitations under the License. package analyze import ( - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" + deploy "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/helm" ) // helmAnalyzer is a Visitor during the directory analysis that finds helm charts diff --git a/pkg/skaffold/initializer/analyze/kustomize.go b/pkg/skaffold/initializer/analyze/kustomize.go index 5e5fb4b7036..a0f310f5855 100644 --- a/pkg/skaffold/initializer/analyze/kustomize.go +++ b/pkg/skaffold/initializer/analyze/kustomize.go @@ -19,7 +19,7 @@ package analyze import ( "path/filepath" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" + deploy "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kustomize" ) // kustomizeAnalyzer is a Visitor during the directory analysis that finds kustomize files diff --git a/pkg/skaffold/initializer/deploy/kustomize.go b/pkg/skaffold/initializer/deploy/kustomize.go index 653fe8c8634..27d30b85e3e 100644 --- a/pkg/skaffold/initializer/deploy/kustomize.go +++ b/pkg/skaffold/initializer/deploy/kustomize.go @@ -21,7 +21,7 @@ import ( "github.com/sirupsen/logrus" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" + pkgkustomize "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kustomize" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/initializer/errors" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" @@ -61,7 +61,7 @@ func (k *kustomize) DeployConfig() (latest.DeployConfig, []latest.Profile) { // if there's only one kustomize path, either leave it blank (if it's the default path), // or generate a config with that single path and return it if len(k.kustomizations) == 1 { - if k.kustomizations[0] == deploy.DefaultKustomizePath { + if k.kustomizations[0] == pkgkustomize.DefaultKustomizePath { kustomizeConfig = &latest.KustomizeDeploy{} } else { kustomizeConfig = &latest.KustomizeDeploy{ diff --git a/pkg/skaffold/kubernetes/manifest/gcs.go b/pkg/skaffold/kubernetes/manifest/gcs.go new file mode 100644 index 00000000000..3962cc594fa --- /dev/null +++ b/pkg/skaffold/kubernetes/manifest/gcs.go @@ -0,0 +1,44 @@ +/* +Copyright 2020 The Skaffold Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package manifest + +import ( + "context" + "fmt" + "os" + "strings" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" +) + +// DownloadFromGCS downloads all provided manifests from a remote GCS bucket, +// and returns a relative path pointing to the GCS temp dir. +func DownloadFromGCS(manifests []string) (string, error) { + if err := os.MkdirAll(ManifestTmpDir, os.ModePerm); err != nil { + return "", fmt.Errorf("failed to create the tmp directory: %w", err) + } + for _, manifest := range manifests { + if manifest == "" || !strings.HasPrefix(manifest, gcsPrefix) { + return "", fmt.Errorf("%v is not a valid GCS path", manifest) + } + gcs := util.Gsutil{} + if err := gcs.Copy(context.Background(), manifest, ManifestTmpDir, true); err != nil { + return "", fmt.Errorf("failed to download manifests fom GCS: %w", err) + } + } + return ManifestTmpDir, nil +} diff --git a/pkg/skaffold/deploy/kubectl/images.go b/pkg/skaffold/kubernetes/manifest/images.go similarity index 97% rename from pkg/skaffold/deploy/kubectl/images.go rename to pkg/skaffold/kubernetes/manifest/images.go index 07986fe1089..0d845dc468a 100644 --- a/pkg/skaffold/deploy/kubectl/images.go +++ b/pkg/skaffold/kubernetes/manifest/images.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubectl +package manifest import ( "fmt" @@ -65,7 +65,7 @@ func (l *ManifestList) ReplaceImages(builds []build.Artifact) (ManifestList, err updated, err := l.Visit(replacer) if err != nil { - return nil, fmt.Errorf("replacing images: %w", err) + return nil, fmt.Errorf("replacing images in manifest: %w", err) } replacer.Check() diff --git a/pkg/skaffold/deploy/kubectl/images_test.go b/pkg/skaffold/kubernetes/manifest/images_test.go similarity index 99% rename from pkg/skaffold/deploy/kubectl/images_test.go rename to pkg/skaffold/kubernetes/manifest/images_test.go index 20e1c09654c..95b79ff3eef 100644 --- a/pkg/skaffold/deploy/kubectl/images_test.go +++ b/pkg/skaffold/kubernetes/manifest/images_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubectl +package manifest import ( "testing" diff --git a/pkg/skaffold/deploy/kubectl/labels.go b/pkg/skaffold/kubernetes/manifest/labels.go similarity index 99% rename from pkg/skaffold/deploy/kubectl/labels.go rename to pkg/skaffold/kubernetes/manifest/labels.go index 731e6870b13..e45dd08bc6d 100644 --- a/pkg/skaffold/deploy/kubectl/labels.go +++ b/pkg/skaffold/kubernetes/manifest/labels.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubectl +package manifest import ( "fmt" diff --git a/pkg/skaffold/deploy/kubectl/labels_test.go b/pkg/skaffold/kubernetes/manifest/labels_test.go similarity index 99% rename from pkg/skaffold/deploy/kubectl/labels_test.go rename to pkg/skaffold/kubernetes/manifest/labels_test.go index 499b64c1c1f..803f762e061 100644 --- a/pkg/skaffold/deploy/kubectl/labels_test.go +++ b/pkg/skaffold/kubernetes/manifest/labels_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubectl +package manifest import ( "testing" diff --git a/pkg/skaffold/deploy/kubectl/manifests.go b/pkg/skaffold/kubernetes/manifest/manifests.go similarity index 98% rename from pkg/skaffold/deploy/kubectl/manifests.go rename to pkg/skaffold/kubernetes/manifest/manifests.go index 424b17d7473..15b4e8bf82e 100644 --- a/pkg/skaffold/deploy/kubectl/manifests.go +++ b/pkg/skaffold/kubernetes/manifest/manifests.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubectl +package manifest import ( "bytes" @@ -24,6 +24,7 @@ import ( ) // ManifestList is a list of yaml manifests. +//nolint:golint type ManifestList [][]byte func (l *ManifestList) String() string { diff --git a/pkg/skaffold/deploy/kubectl/manifests_test.go b/pkg/skaffold/kubernetes/manifest/manifests_test.go similarity index 99% rename from pkg/skaffold/deploy/kubectl/manifests_test.go rename to pkg/skaffold/kubernetes/manifest/manifests_test.go index 47dc54916f6..9c3c5a7465a 100644 --- a/pkg/skaffold/deploy/kubectl/manifests_test.go +++ b/pkg/skaffold/kubernetes/manifest/manifests_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubectl +package manifest import ( "testing" diff --git a/pkg/skaffold/deploy/kubectl/namespace_test.go b/pkg/skaffold/kubernetes/manifest/namespace_test.go similarity index 99% rename from pkg/skaffold/deploy/kubectl/namespace_test.go rename to pkg/skaffold/kubernetes/manifest/namespace_test.go index c4d35018e38..c508afa88a5 100644 --- a/pkg/skaffold/deploy/kubectl/namespace_test.go +++ b/pkg/skaffold/kubernetes/manifest/namespace_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubectl +package manifest import ( "testing" diff --git a/pkg/skaffold/deploy/kubectl/namespaces.go b/pkg/skaffold/kubernetes/manifest/namespaces.go similarity index 98% rename from pkg/skaffold/deploy/kubectl/namespaces.go rename to pkg/skaffold/kubernetes/manifest/namespaces.go index 29b3858c88c..55b2687ba1c 100644 --- a/pkg/skaffold/deploy/kubectl/namespaces.go +++ b/pkg/skaffold/kubernetes/manifest/namespaces.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubectl +package manifest import ( "fmt" diff --git a/pkg/skaffold/kubernetes/manifest/transformations.go b/pkg/skaffold/kubernetes/manifest/transformations.go new file mode 100644 index 00000000000..e024563c4bf --- /dev/null +++ b/pkg/skaffold/kubernetes/manifest/transformations.go @@ -0,0 +1,55 @@ +/* +Copyright 2019 The Skaffold Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package manifest + +import ( + "fmt" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" +) + +type Registries struct { + InsecureRegistries map[string]bool + DebugHelpersRegistry string +} + +type Transform func(l ManifestList, builds []build.Artifact, registries Registries) (ManifestList, error) + +// Transforms are applied to manifests +var transforms []Transform + +// AddTransform adds a transform to be applied when deploying. +func AddTransform(newTransform Transform) { + transforms = append(transforms, newTransform) +} + +// GetTransforms returns all manifest transforms. +func GetTransforms() []Transform { + return transforms +} + +// ApplyTransforms applies all manifests transforms to the provided manifests. +func ApplyTransforms(manifests ManifestList, builds []build.Artifact, insecureRegistries map[string]bool, debugHelpersRegistry string) (ManifestList, error) { + var err error + for _, transform := range transforms { + manifests, err = transform(manifests, builds, Registries{insecureRegistries, debugHelpersRegistry}) + if err != nil { + return nil, fmt.Errorf("unable to transform manifests: %w", err) + } + } + return manifests, nil +} diff --git a/pkg/skaffold/kubernetes/manifest/util.go b/pkg/skaffold/kubernetes/manifest/util.go new file mode 100644 index 00000000000..3e280197211 --- /dev/null +++ b/pkg/skaffold/kubernetes/manifest/util.go @@ -0,0 +1,73 @@ +/* +Copyright 2020 The Skaffold Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package manifest + +import ( + "context" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" +) + +const ( + manifestsStagingFolder = "manifest_tmp" + renderedManifestsStagingFile = "rendered_manifest.yaml" + gcsPrefix = "gs://" +) + +var ManifestTmpDir = filepath.Join(os.TempDir(), manifestsStagingFolder) + +// Write writes manifests to a file, a writer or a GCS bucket. +func Write(manifests string, output string, manifestOut io.Writer) error { + switch { + case output == "": + _, err := fmt.Fprintln(manifestOut, manifests) + return err + case strings.HasPrefix(output, gcsPrefix): + tempDir, err := ioutil.TempDir("", manifestsStagingFolder) + if err != nil { + return fmt.Errorf("failed to create the tmp directory: %w", err) + } + defer os.RemoveAll(tempDir) + tempFile := filepath.Join(tempDir, renderedManifestsStagingFile) + if err := dumpToFile(manifests, tempFile); err != nil { + return err + } + gcs := util.Gsutil{} + if err := gcs.Copy(context.Background(), tempFile, output, false); err != nil { + return fmt.Errorf("failed to copy rendered manifests to GCS: %w", err) + } + return nil + default: + return dumpToFile(manifests, output) + } +} + +func dumpToFile(manifests string, filepath string) error { + f, err := os.Create(filepath) + if err != nil { + return fmt.Errorf("opening file for writing manifests: %w", err) + } + defer f.Close() + _, err = f.WriteString(manifests + "\n") + return err +} diff --git a/pkg/skaffold/deploy/kubectl/visitor.go b/pkg/skaffold/kubernetes/manifest/visitor.go similarity index 99% rename from pkg/skaffold/deploy/kubectl/visitor.go rename to pkg/skaffold/kubernetes/manifest/visitor.go index 8d7ce3109ad..0b9bc542aac 100644 --- a/pkg/skaffold/deploy/kubectl/visitor.go +++ b/pkg/skaffold/kubernetes/manifest/visitor.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubectl +package manifest import ( "fmt" diff --git a/pkg/skaffold/deploy/kubectl/visitor_test.go b/pkg/skaffold/kubernetes/manifest/visitor_test.go similarity index 99% rename from pkg/skaffold/deploy/kubectl/visitor_test.go rename to pkg/skaffold/kubernetes/manifest/visitor_test.go index c7cb0e3694c..a6796854775 100644 --- a/pkg/skaffold/deploy/kubectl/visitor_test.go +++ b/pkg/skaffold/kubernetes/manifest/visitor_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubectl +package manifest import ( "fmt" diff --git a/pkg/skaffold/kubernetes/portforward/resource_forwarder_test.go b/pkg/skaffold/kubernetes/portforward/resource_forwarder_test.go index feb38b3829a..ef41e31eb18 100644 --- a/pkg/skaffold/kubernetes/portforward/resource_forwarder_test.go +++ b/pkg/skaffold/kubernetes/portforward/resource_forwarder_test.go @@ -33,7 +33,7 @@ import ( fakekubeclientset "k8s.io/client-go/kubernetes/fake" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/label" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" @@ -275,7 +275,7 @@ func TestRetrieveServices(t *testing.T) { Name: "svc1", Namespace: "test", Labels: map[string]string{ - deploy.RunIDLabel: "9876-6789", + label.RunIDLabel: "9876-6789", }, }, Spec: v1.ServiceSpec{Ports: []v1.ServicePort{{Port: 8080}}}, @@ -284,7 +284,7 @@ func TestRetrieveServices(t *testing.T) { Name: "svc2", Namespace: "test1", Labels: map[string]string{ - deploy.RunIDLabel: "9876-6789", + label.RunIDLabel: "9876-6789", }, }, Spec: v1.ServiceSpec{Ports: []v1.ServicePort{{Port: 8081}}}, @@ -314,7 +314,7 @@ func TestRetrieveServices(t *testing.T) { Name: "svc1", Namespace: "test", Labels: map[string]string{ - deploy.RunIDLabel: "9876-6789", + label.RunIDLabel: "9876-6789", }, }, Spec: v1.ServiceSpec{Ports: []v1.ServicePort{{Port: 8080}}}, @@ -329,7 +329,7 @@ func TestRetrieveServices(t *testing.T) { Name: "svc1", Namespace: "test", Labels: map[string]string{ - deploy.RunIDLabel: "9876-6789", + label.RunIDLabel: "9876-6789", }, }, }, @@ -346,7 +346,7 @@ func TestRetrieveServices(t *testing.T) { client := fakekubeclientset.NewSimpleClientset(objs...) t.Override(&kubernetesclient.Client, mockClient(client)) - actual, err := retrieveServiceResources(fmt.Sprintf("%s=9876-6789", deploy.RunIDLabel), test.namespaces) + actual, err := retrieveServiceResources(fmt.Sprintf("%s=9876-6789", label.RunIDLabel), test.namespaces) t.CheckNoError(err) t.CheckDeepEqual(test.expected, actual) diff --git a/pkg/skaffold/runner/build_deploy.go b/pkg/skaffold/runner/build_deploy.go index cad5dd672c2..92861851c02 100644 --- a/pkg/skaffold/runner/build_deploy.go +++ b/pkg/skaffold/runner/build_deploy.go @@ -28,7 +28,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" + deployutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/util" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" ) @@ -144,7 +144,7 @@ type tagErr struct { // ApplyDefaultRepo applies the default repo to a given image tag. func (r *SkaffoldRunner) ApplyDefaultRepo(tag string) (string, error) { - return deploy.ApplyDefaultRepo(r.runCtx.GlobalConfig(), r.runCtx.DefaultRepo(), tag) + return deployutil.ApplyDefaultRepo(r.runCtx.GlobalConfig(), r.runCtx.DefaultRepo(), tag) } // imageTags generates tags for a list of artifacts diff --git a/pkg/skaffold/runner/deploy.go b/pkg/skaffold/runner/deploy.go index 1e38084bae2..be8024a8d62 100644 --- a/pkg/skaffold/runner/deploy.go +++ b/pkg/skaffold/runner/deploy.go @@ -28,7 +28,7 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" + deployutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/util" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" kubectx "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/context" @@ -66,7 +66,7 @@ See https://skaffold.dev/docs/pipeline-stages/taggers/#how-tagging-works`) } } - deployOut, postDeployFn, err := deploy.WithLogFile(time.Now().Format(deploy.TimeFormat)+".log", out, r.runCtx.Muted()) + deployOut, postDeployFn, err := deployutil.WithLogFile(time.Now().Format(deployutil.TimeFormat)+".log", out, r.runCtx.Muted()) if err != nil { return err } diff --git a/pkg/skaffold/runner/deploy_test.go b/pkg/skaffold/runner/deploy_test.go index fadbbe39088..0a0bfa9aa53 100644 --- a/pkg/skaffold/runner/deploy_test.go +++ b/pkg/skaffold/runner/deploy_test.go @@ -29,7 +29,8 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/label" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/status" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" @@ -67,7 +68,7 @@ func TestDeploy(t *testing.T) { testutil.Run(t, test.description, func(t *testutil.T) { t.SetupFakeKubernetesContext(api.Config{CurrentContext: "cluster1"}) t.Override(&client.Client, mockK8sClient) - t.Override(&newStatusCheck, func(deploy.StatusCheckConfig, *deploy.DefaultLabeller) deploy.StatusChecker { + t.Override(&newStatusCheck, func(status.Config, *label.DefaultLabeller) status.Checker { return dummyStatusChecker{} }) @@ -117,7 +118,7 @@ func TestDeployNamespace(t *testing.T) { testutil.Run(t, test.description, func(t *testutil.T) { t.SetupFakeKubernetesContext(api.Config{CurrentContext: "cluster1"}) t.Override(&client.Client, mockK8sClient) - t.Override(&newStatusCheck, func(deploy.StatusCheckConfig, *deploy.DefaultLabeller) deploy.StatusChecker { + t.Override(&newStatusCheck, func(status.Config, *label.DefaultLabeller) status.Checker { return dummyStatusChecker{} }) diff --git a/pkg/skaffold/runner/new.go b/pkg/skaffold/runner/new.go index 905cc7879b5..ff86b777212 100644 --- a/pkg/skaffold/runner/new.go +++ b/pkg/skaffold/runner/new.go @@ -29,9 +29,14 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/local" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/helm" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kpt" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kustomize" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/label" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/filemon" - "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" + pkgkubectl "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" @@ -43,7 +48,7 @@ import ( // NewForConfig returns a new SkaffoldRunner for a SkaffoldConfig func NewForConfig(runCtx *runcontext.RunContext) (*SkaffoldRunner, error) { - kubectlCLI := kubectl.NewCLI(runCtx, "") + kubectlCLI := pkgkubectl.NewCLI(runCtx, "") tagger, err := getTagger(runCtx) if err != nil { @@ -60,7 +65,7 @@ func NewForConfig(runCtx *runcontext.RunContext) (*SkaffoldRunner, error) { tryImportMissing = localBuilder.TryImportMissing() } - labeller := deploy.NewLabeller(runCtx.AddSkaffoldLabels(), runCtx.CustomLabels()) + labeller := label.NewLabeller(runCtx.AddSkaffoldLabels(), runCtx.CustomLabels()) tester := getTester(runCtx, imagesAreLocal) syncer := getSyncer(runCtx) var deployer deploy.Deployer @@ -196,21 +201,21 @@ func getSyncer(cfg sync.Config) sync.Syncer { return sync.NewSyncer(cfg) } -func getDeployer(cfg deploy.Config, labels map[string]string) (deploy.Deployer, error) { +func getDeployer(cfg kubectl.Config, labels map[string]string) (deploy.Deployer, error) { d := cfg.Pipeline().Deploy var deployers deploy.DeployerMux if d.HelmDeploy != nil { - deployers = append(deployers, deploy.NewHelmDeployer(cfg, labels)) + deployers = append(deployers, helm.NewDeployer(cfg, labels)) } if d.KptDeploy != nil { - deployers = append(deployers, deploy.NewKptDeployer(cfg, labels)) + deployers = append(deployers, kpt.NewDeployer(cfg, labels)) } if d.KubectlDeploy != nil { - deployer, err := deploy.NewKubectlDeployer(cfg, labels) + deployer, err := kubectl.NewDeployer(cfg, labels) if err != nil { return nil, err } @@ -218,7 +223,7 @@ func getDeployer(cfg deploy.Config, labels map[string]string) (deploy.Deployer, } if d.KustomizeDeploy != nil { - deployer, err := deploy.NewKustomizeDeployer(cfg, labels) + deployer, err := kustomize.NewDeployer(cfg, labels) if err != nil { return nil, err } diff --git a/pkg/skaffold/runner/new_test.go b/pkg/skaffold/runner/new_test.go index b47e464dac7..4fa7d8b3e96 100644 --- a/pkg/skaffold/runner/new_test.go +++ b/pkg/skaffold/runner/new_test.go @@ -22,6 +22,10 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/helm" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kpt" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kustomize" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/testutil" @@ -41,12 +45,12 @@ func TestGetDeployer(tOuter *testing.T) { { description: "helm deployer", cfg: latest.DeployType{HelmDeploy: &latest.HelmDeploy{}}, - expected: deploy.NewHelmDeployer(&runcontext.RunContext{}, nil), + expected: helm.NewDeployer(&runcontext.RunContext{}, nil), }, { description: "kubectl deployer", cfg: latest.DeployType{KubectlDeploy: &latest.KubectlDeploy{}}, - expected: t.RequireNonNilResult(deploy.NewKubectlDeployer(&runcontext.RunContext{ + expected: t.RequireNonNilResult(kubectl.NewDeployer(&runcontext.RunContext{ Cfg: latest.Pipeline{ Deploy: latest.DeployConfig{ DeployType: latest.DeployType{ @@ -61,7 +65,7 @@ func TestGetDeployer(tOuter *testing.T) { { description: "kustomize deployer", cfg: latest.DeployType{KustomizeDeploy: &latest.KustomizeDeploy{}}, - expected: t.RequireNonNilResult(deploy.NewKustomizeDeployer(&runcontext.RunContext{ + expected: t.RequireNonNilResult(kustomize.NewDeployer(&runcontext.RunContext{ Cfg: latest.Pipeline{ Deploy: latest.DeployConfig{ DeployType: latest.DeployType{ @@ -76,7 +80,7 @@ func TestGetDeployer(tOuter *testing.T) { { description: "kpt deployer", cfg: latest.DeployType{KptDeploy: &latest.KptDeploy{}}, - expected: deploy.NewKptDeployer(&runcontext.RunContext{}, nil), + expected: kpt.NewDeployer(&runcontext.RunContext{}, nil), }, { description: "multiple deployers", @@ -85,8 +89,8 @@ func TestGetDeployer(tOuter *testing.T) { KptDeploy: &latest.KptDeploy{}, }, expected: deploy.DeployerMux{ - deploy.NewHelmDeployer(&runcontext.RunContext{}, nil), - deploy.NewKptDeployer(&runcontext.RunContext{}, nil), + helm.NewDeployer(&runcontext.RunContext{}, nil), + kpt.NewDeployer(&runcontext.RunContext{}, nil), }, }, } diff --git a/pkg/skaffold/runner/runner.go b/pkg/skaffold/runner/runner.go index a772f4b5391..62c31d72394 100644 --- a/pkg/skaffold/runner/runner.go +++ b/pkg/skaffold/runner/runner.go @@ -24,6 +24,8 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/cache" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/label" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/status" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/filemon" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" @@ -66,7 +68,7 @@ type SkaffoldRunner struct { cache cache.Cache changeSet changeSet runCtx *runcontext.RunContext - labeller *deploy.DefaultLabeller + labeller *label.DefaultLabeller builds []build.Artifact // podSelector is used to determine relevant pods for logging and portForwarding @@ -81,7 +83,7 @@ type SkaffoldRunner struct { // for testing var ( - newStatusCheck = deploy.NewStatusChecker + newStatusCheck = status.NewStatusChecker ) // HasDeployed returns true if this runner has deployed something. diff --git a/pkg/skaffold/runner/runner_test.go b/pkg/skaffold/runner/runner_test.go index 191d928832f..deea07aaf77 100644 --- a/pkg/skaffold/runner/runner_test.go +++ b/pkg/skaffold/runner/runner_test.go @@ -29,6 +29,9 @@ import ( "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/helm" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kustomize" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/filemon" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/defaults" @@ -276,7 +279,7 @@ func TestNewForConfig(t *testing.T) { }, expectedBuilder: &local.Builder{}, expectedTester: &test.FullTester{}, - expectedDeployer: &deploy.KubectlDeployer{}, + expectedDeployer: &kubectl.Deployer{}, }, { description: "bad tagger config", @@ -301,7 +304,7 @@ func TestNewForConfig(t *testing.T) { shouldErr: true, expectedBuilder: &local.Builder{}, expectedTester: &test.FullTester{}, - expectedDeployer: &deploy.KubectlDeployer{}, + expectedDeployer: &kubectl.Deployer{}, }, { description: "no artifacts, cache", @@ -320,7 +323,7 @@ func TestNewForConfig(t *testing.T) { }, expectedBuilder: &local.Builder{}, expectedTester: &test.FullTester{}, - expectedDeployer: &deploy.KubectlDeployer{}, + expectedDeployer: &kubectl.Deployer{}, cacheArtifacts: true, }, { @@ -343,9 +346,9 @@ func TestNewForConfig(t *testing.T) { expectedBuilder: &local.Builder{}, expectedTester: &test.FullTester{}, expectedDeployer: deploy.DeployerMux([]deploy.Deployer{ - &deploy.HelmDeployer{}, - &deploy.KubectlDeployer{}, - &deploy.KustomizeDeployer{}, + &helm.Deployer{}, + &kubectl.Deployer{}, + &kustomize.Deployer{}, }), }, }