From 567264627878d9f1acc03ab2069e462da820c57e Mon Sep 17 00:00:00 2001 From: Stefan Prodan Date: Thu, 8 Oct 2020 12:30:43 +0300 Subject: [PATCH] Use install pkg in CLI Signed-off-by: Stefan Prodan --- cmd/gotk/bootstrap.go | 42 +++-- cmd/gotk/install.go | 315 +++++------------------------------- pkg/install/install.go | 16 +- pkg/install/install_test.go | 4 +- 4 files changed, 73 insertions(+), 304 deletions(-) diff --git a/cmd/gotk/bootstrap.go b/cmd/gotk/bootstrap.go index b82a704e..91c3fa22 100644 --- a/cmd/gotk/bootstrap.go +++ b/cmd/gotk/bootstrap.go @@ -19,6 +19,7 @@ package main import ( "context" "fmt" + "io/ioutil" "net/url" "os" "path" @@ -36,6 +37,8 @@ import ( kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" + + "github.com/fluxcd/toolkit/pkg/install" ) var bootstrapCmd = &cobra.Command{ @@ -111,31 +114,36 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes if err := os.MkdirAll(manifestsDir, os.ModePerm); err != nil { return "", fmt.Errorf("creating manifests dir failed: %w", err) } - manifest := path.Join(manifestsDir, bootstrapInstallManifest) - if localManifests != "" { - if err := buildKustomization(localManifests, manifest); err != nil { - return "", fmt.Errorf("build kustomization failed: %w", err) - } + manifest := path.Join(manifestsDir, bootstrapInstallManifest) - return manifest, nil + opts := install.Options{ + BaseURL: localManifests, + Version: bootstrapVersion, + Namespace: namespace, + Components: bootstrapComponents, + Registry: bootstrapRegistry, + ImagePullSecret: bootstrapImagePullSecret, + Arch: bootstrapArch, + WatchAllNamespaces: bootstrapWatchAllNamespaces, + NetworkPolicy: bootstrapNetworkPolicy, + LogLevel: bootstrapLogLevel, + NotificationController: defaultNotification, + ManifestsFile: fmt.Sprintf("%s.yaml", namespace), + Timeout: timeout, } - gotkDir := path.Join(tmpDir, ".gotk") - defer os.RemoveAll(gotkDir) - - if err := os.MkdirAll(gotkDir, os.ModePerm); err != nil { - return "", fmt.Errorf("generating manifests failed: %w", err) + if localManifests == "" { + opts.BaseURL = install.MakeDefaultOptions().BaseURL } - if err := genInstallManifests(bootstrapVersion, namespace, bootstrapComponents, - bootstrapWatchAllNamespaces, bootstrapNetworkPolicy, bootstrapRegistry, bootstrapImagePullSecret, - bootstrapArch, bootstrapLogLevel, gotkDir); err != nil { - return "", fmt.Errorf("generating manifests failed: %w", err) + output, err := install.Generate(opts) + if err != nil { + return "", fmt.Errorf("generating install manifests failed: %w", err) } - if err := buildKustomization(gotkDir, manifest); err != nil { - return "", fmt.Errorf("build kustomization failed: %w", err) + if err := ioutil.WriteFile(manifest, output, os.ModePerm); err != nil { + return "", fmt.Errorf("generating install manifests failed: %w", err) } return manifest, nil diff --git a/cmd/gotk/install.go b/cmd/gotk/install.go index 5ecc3b59..5aefbcb2 100644 --- a/cmd/gotk/install.go +++ b/cmd/gotk/install.go @@ -20,18 +20,13 @@ import ( "context" "fmt" "io/ioutil" - "net/http" "os" "path" - "path/filepath" "strings" - "time" "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/api/filesys" - "sigs.k8s.io/kustomize/api/krusty" - "github.com/fluxcd/pkg/untar" + "github.com/fluxcd/toolkit/pkg/install" ) var installCmd = &cobra.Command{ @@ -114,38 +109,50 @@ func installCmdRun(cmd *cobra.Command, args []string) error { if !installExport { logger.Generatef("generating manifests") } + + opts := install.Options{ + BaseURL: installManifestsPath, + Version: installVersion, + Namespace: namespace, + Components: installComponents, + Registry: installRegistry, + ImagePullSecret: installImagePullSecret, + Arch: installArch, + WatchAllNamespaces: installWatchAllNamespaces, + NetworkPolicy: installNetworkPolicy, + LogLevel: installLogLevel, + NotificationController: defaultNotification, + ManifestsFile: fmt.Sprintf("%s.yaml", namespace), + Timeout: timeout, + } + if installManifestsPath == "" { - err = genInstallManifests(installVersion, namespace, installComponents, - installWatchAllNamespaces, installNetworkPolicy, installRegistry, installImagePullSecret, - installArch, installLogLevel, tmpDir) - if err != nil { - return fmt.Errorf("install failed: %w", err) - } - installManifestsPath = tmpDir + opts.BaseURL = install.MakeDefaultOptions().BaseURL } - manifest := path.Join(tmpDir, fmt.Sprintf("%s.yaml", namespace)) - if err := buildKustomization(installManifestsPath, manifest); err != nil { + output, err := install.Generate(opts) + if err != nil { return fmt.Errorf("install failed: %w", err) } - command := fmt.Sprintf("cat %s", manifest) - if yaml, err := utils.execCommand(ctx, ModeCapture, command); err != nil { + manifest := path.Join(tmpDir, fmt.Sprintf("%s.yaml", namespace)) + if err := ioutil.WriteFile(manifest, output, os.ModePerm); err != nil { return fmt.Errorf("install failed: %w", err) - } else { - if verbose { - fmt.Print(yaml) - } else if installExport { - fmt.Println("---") - fmt.Println("# GitOps Toolkit revision", installVersion, time.Now().Format(time.RFC3339)) - fmt.Println("# Components:", strings.Join(installComponents, ",")) - fmt.Print(yaml) - fmt.Println("---") - return nil - } } - logger.Successf("manifests build completed") + yaml := string(output) + if verbose { + fmt.Print(yaml) + } else if installExport { + fmt.Println("---") + fmt.Println("# GitOps Toolkit revision", installVersion) + fmt.Println("# Components:", strings.Join(installComponents, ",")) + fmt.Print(yaml) + fmt.Println("---") + return nil + } + + logger.Successf("manifests build completed") logger.Actionf("installing components in %s namespace", namespace) applyOutput := ModeStderrOS if verbose { @@ -156,7 +163,8 @@ func installCmdRun(cmd *cobra.Command, args []string) error { dryRun = "--dry-run=client" applyOutput = ModeOS } - command = fmt.Sprintf("cat %s | kubectl apply -f- %s", manifest, dryRun) + + command := fmt.Sprintf("kubectl apply -f %s %s", manifest, dryRun) if _, err := utils.execCommand(ctx, applyOutput, command); err != nil { return fmt.Errorf("install failed") } @@ -182,250 +190,3 @@ func installCmdRun(cmd *cobra.Command, args []string) error { logger.Successf("install finished") return nil } - -var namespaceTmpl = `--- -apiVersion: v1 -kind: Namespace -metadata: - name: {{.Namespace}} -` - -var labelsTmpl = `--- -apiVersion: builtin -kind: LabelTransformer -metadata: - name: labels -labels: - app.kubernetes.io/instance: {{.Namespace}} - app.kubernetes.io/version: "{{.Version}}" -fieldSpecs: - - path: metadata/labels - create: true -` - -var kustomizationTmpl = `--- -{{- $eventsAddr := .EventsAddr }} -{{- $watchAllNamespaces := .WatchAllNamespaces }} -{{- $registry := .Registry }} -{{- $arch := .Arch }} -{{- $logLevel := .LogLevel }} -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -namespace: {{.Namespace}} - -transformers: - - labels.yaml - -resources: - - namespace.yaml -{{- if .NetworkPolicy }} - - policies.yaml -{{- end }} - - roles -{{- range .Components }} - - {{.}}.yaml -{{- end }} - -patches: -- path: node-selector.yaml - target: - kind: Deployment - -patchesJson6902: -{{- range $i, $component := .Components }} -{{- if eq $component "notification-controller" }} -- target: - group: apps - version: v1 - kind: Deployment - name: {{$component}} - patch: |- - - op: replace - path: /spec/template/spec/containers/0/args/0 - value: --watch-all-namespaces={{$watchAllNamespaces}} - - op: replace - path: /spec/template/spec/containers/0/args/1 - value: --log-level={{$logLevel}} -{{- else }} -- target: - group: apps - version: v1 - kind: Deployment - name: {{$component}} - patch: |- - - op: replace - path: /spec/template/spec/containers/0/args/0 - value: --events-addr={{$eventsAddr}} - - op: replace - path: /spec/template/spec/containers/0/args/1 - value: --watch-all-namespaces={{$watchAllNamespaces}} - - op: replace - path: /spec/template/spec/containers/0/args/2 - value: --log-level={{$logLevel}} -{{- end }} -{{- end }} - -{{- if $registry }} -images: -{{- range $i, $component := .Components }} - - name: fluxcd/{{$component}} -{{- if eq $arch "amd64" }} - newName: {{$registry}}/{{$component}} -{{- else }} - newName: {{$registry}}/{{$component}}-arm64 -{{- end }} -{{- end }} -{{- end }} -` - -var kustomizationRolesTmpl = `--- -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: - - rbac.yaml -nameSuffix: -{{.Namespace}} -` - -var nodeSelectorTmpl = `--- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: all -spec: - template: - spec: - nodeSelector: - kubernetes.io/arch: {{.Arch}} - kubernetes.io/os: linux -{{- if .ImagePullSecret }} - imagePullSecrets: - - name: {{.ImagePullSecret}} -{{- end }} -` - -func downloadManifests(version string, tmpDir string) error { - ghURL := "https://github.com/fluxcd/toolkit/releases/latest/download/manifests.tar.gz" - if strings.HasPrefix(version, "v") { - ghURL = fmt.Sprintf("https://github.com/fluxcd/toolkit/releases/download/%s/manifests.tar.gz", version) - } - - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - req, err := http.NewRequest("GET", ghURL, nil) - if err != nil { - return fmt.Errorf("failed to create HTTP request for %s, error: %w", ghURL, err) - } - - // download - resp, err := http.DefaultClient.Do(req.WithContext(ctx)) - if err != nil { - return fmt.Errorf("failed to download artifact from %s, error: %w", ghURL, err) - } - defer resp.Body.Close() - - // check response - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("faild to download artifact from %s, status: %s", ghURL, resp.Status) - } - - // extract - if _, err = untar.Untar(resp.Body, tmpDir); err != nil { - return fmt.Errorf("faild to untar manifests from %s, error: %w", ghURL, err) - } - - return nil -} - -func genInstallManifests(version string, namespace string, components []string, - watchAllNamespaces, networkPolicy bool, registry, imagePullSecret, arch, logLevel, tmpDir string) error { - eventsAddr := "" - if utils.containsItemString(components, defaultNotification) { - eventsAddr = fmt.Sprintf("http://%s/", defaultNotification) - } - - model := struct { - Version string - Namespace string - Components []string - EventsAddr string - Registry string - ImagePullSecret string - Arch string - WatchAllNamespaces bool - NetworkPolicy bool - LogLevel string - }{ - Version: version, - Namespace: namespace, - Components: components, - EventsAddr: eventsAddr, - Registry: registry, - ImagePullSecret: imagePullSecret, - Arch: arch, - WatchAllNamespaces: watchAllNamespaces, - NetworkPolicy: networkPolicy, - LogLevel: logLevel, - } - - if err := downloadManifests(version, tmpDir); err != nil { - return err - } - - if err := utils.execTemplate(model, namespaceTmpl, path.Join(tmpDir, "namespace.yaml")); err != nil { - return fmt.Errorf("generate namespace failed: %w", err) - } - - if err := utils.execTemplate(model, labelsTmpl, path.Join(tmpDir, "labels.yaml")); err != nil { - return fmt.Errorf("generate labels failed: %w", err) - } - - if err := utils.execTemplate(model, nodeSelectorTmpl, path.Join(tmpDir, "node-selector.yaml")); err != nil { - return fmt.Errorf("generate node selector failed: %w", err) - } - - if err := utils.execTemplate(model, kustomizationTmpl, path.Join(tmpDir, "kustomization.yaml")); err != nil { - return fmt.Errorf("generate kustomization failed: %w", err) - } - - if err := os.MkdirAll(path.Join(tmpDir, "roles"), os.ModePerm); err != nil { - return fmt.Errorf("generate roles failed: %w", err) - } - - if err := utils.execTemplate(model, kustomizationRolesTmpl, path.Join(tmpDir, "roles/kustomization.yaml")); err != nil { - return fmt.Errorf("generate roles failed: %w", err) - } - - if err := utils.copyFile(filepath.Join(tmpDir, "rbac.yaml"), filepath.Join(tmpDir, "roles/rbac.yaml")); err != nil { - return fmt.Errorf("generate rbac failed: %w", err) - } - - return nil -} - -func buildKustomization(base, manifests string) error { - kfile := filepath.Join(base, "kustomization.yaml") - - fs := filesys.MakeFsOnDisk() - if !fs.Exists(kfile) { - return fmt.Errorf("%s not found", kfile) - } - - opt := krusty.MakeDefaultOptions() - k := krusty.MakeKustomizer(fs, opt) - m, err := k.Run(base) - if err != nil { - return err - } - - resources, err := m.AsYaml() - if err != nil { - return err - } - - if err := fs.WriteFile(manifests, resources); err != nil { - return err - } - - return nil -} diff --git a/pkg/install/install.go b/pkg/install/install.go index e7b499aa..0d267394 100644 --- a/pkg/install/install.go +++ b/pkg/install/install.go @@ -28,13 +28,13 @@ import ( // Generate returns the install manifests as a multi-doc YAML. // The manifests are built from a GitHub release or from a // Kustomize overlay if the supplied Options.BaseURL is a local path. -func Generate(options Options) (string, error) { +func Generate(options Options) ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), options.Timeout) defer cancel() tmpDir, err := ioutil.TempDir("", options.Namespace) if err != nil { - return "", fmt.Errorf("temp dir error: %w", err) + return nil, fmt.Errorf("temp dir error: %w", err) } defer os.RemoveAll(tmpDir) @@ -42,26 +42,26 @@ func Generate(options Options) (string, error) { if !strings.HasPrefix(options.BaseURL, "http") { if err := build(options.BaseURL, output); err != nil { - return "", err + return nil, err } } else { if err := fetch(ctx, options.BaseURL, options.Version, tmpDir); err != nil { - return "", err + return nil, err } if err := generate(tmpDir, options); err != nil { - return "", err + return nil, err } if err := build(tmpDir, output); err != nil { - return "", err + return nil, err } } content, err := ioutil.ReadFile(output) if err != nil { - return "", err + return nil, err } - return string(content), nil + return content, nil } diff --git a/pkg/install/install_test.go b/pkg/install/install_test.go index 765d8c8f..b60d1799 100644 --- a/pkg/install/install_test.go +++ b/pkg/install/install_test.go @@ -31,10 +31,10 @@ func TestGenerate(t *testing.T) { for _, component := range opts.Components { img := fmt.Sprintf("%s/%s", opts.Registry, component) - if !strings.Contains(output, img) { + if !strings.Contains(string(output), img) { t.Errorf("component image '%s' not found", img) } } - fmt.Println(output) + fmt.Println(string(output)) }