From c4d3fa7a48844b1287eddd10aad5caea4bc2e2aa Mon Sep 17 00:00:00 2001 From: Stefan Prodan Date: Wed, 7 Oct 2020 22:52:28 +0300 Subject: [PATCH 1/2] init install pkg Signed-off-by: Stefan Prodan --- pkg/install/install.go | 67 +++++++++++++ pkg/install/install_test.go | 40 ++++++++ pkg/install/manifests.go | 125 +++++++++++++++++++++++ pkg/install/options.go | 64 ++++++++++++ pkg/install/templates.go | 195 ++++++++++++++++++++++++++++++++++++ 5 files changed, 491 insertions(+) create mode 100644 pkg/install/install.go create mode 100644 pkg/install/install_test.go create mode 100644 pkg/install/manifests.go create mode 100644 pkg/install/options.go create mode 100644 pkg/install/templates.go diff --git a/pkg/install/install.go b/pkg/install/install.go new file mode 100644 index 00000000..e7b499aa --- /dev/null +++ b/pkg/install/install.go @@ -0,0 +1,67 @@ +/* +Copyright 2020 The Flux CD contributors. + +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 install + +import ( + "context" + "fmt" + "io/ioutil" + "os" + "path" + "strings" +) + +// 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) { + 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) + } + defer os.RemoveAll(tmpDir) + + output := path.Join(tmpDir, options.ManifestsFile) + + if !strings.HasPrefix(options.BaseURL, "http") { + if err := build(options.BaseURL, output); err != nil { + return "", err + } + } else { + if err := fetch(ctx, options.BaseURL, options.Version, tmpDir); err != nil { + return "", err + } + + if err := generate(tmpDir, options); err != nil { + return "", err + } + + if err := build(tmpDir, output); err != nil { + return "", err + } + } + + content, err := ioutil.ReadFile(output) + if err != nil { + return "", err + } + + return string(content), nil +} diff --git a/pkg/install/install_test.go b/pkg/install/install_test.go new file mode 100644 index 00000000..765d8c8f --- /dev/null +++ b/pkg/install/install_test.go @@ -0,0 +1,40 @@ +/* +Copyright 2020 The Flux CD contributors. + +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 install + +import ( + "fmt" + "strings" + "testing" +) + +func TestGenerate(t *testing.T) { + opts := MakeDefaultOptions() + output, err := Generate(opts) + if err != nil { + t.Fatal(err) + } + + for _, component := range opts.Components { + img := fmt.Sprintf("%s/%s", opts.Registry, component) + if !strings.Contains(output, img) { + t.Errorf("component image '%s' not found", img) + } + } + + fmt.Println(output) +} diff --git a/pkg/install/manifests.go b/pkg/install/manifests.go new file mode 100644 index 00000000..e9dcfac3 --- /dev/null +++ b/pkg/install/manifests.go @@ -0,0 +1,125 @@ +/* +Copyright 2020 The Flux CD contributors. + +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 install + +import ( + "context" + "fmt" + "net/http" + "os" + "path" + "path/filepath" + "strings" + + "sigs.k8s.io/kustomize/api/filesys" + "sigs.k8s.io/kustomize/api/krusty" + + "github.com/fluxcd/pkg/untar" +) + +func fetch(ctx context.Context, url, version, dir string) error { + ghURL := fmt.Sprintf("%s/latest/download/manifests.tar.gz", url) + if strings.HasPrefix(version, "v") { + ghURL = fmt.Sprintf("%s/download/%s/manifests.tar.gz", url, version) + } + + 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 manifests.tar.gz from %s, error: %w", ghURL, err) + } + defer resp.Body.Close() + + // check response + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("faild to download manifests.tar.gz from %s, status: %s", ghURL, resp.Status) + } + + // extract + if _, err = untar.Untar(resp.Body, dir); err != nil { + return fmt.Errorf("faild to untar manifests.tar.gz from %s, error: %w", ghURL, err) + } + + return nil +} + +func generate(base string, options Options) error { + if containsItemString(options.Components, options.NotificationController) { + options.EventsAddr = fmt.Sprintf("http://%s/", options.NotificationController) + } + + if err := execTemplate(options, namespaceTmpl, path.Join(base, "namespace.yaml")); err != nil { + return fmt.Errorf("generate namespace failed: %w", err) + } + + if err := execTemplate(options, labelsTmpl, path.Join(base, "labels.yaml")); err != nil { + return fmt.Errorf("generate labels failed: %w", err) + } + + if err := execTemplate(options, nodeSelectorTmpl, path.Join(base, "node-selector.yaml")); err != nil { + return fmt.Errorf("generate node selector failed: %w", err) + } + + if err := execTemplate(options, kustomizationTmpl, path.Join(base, "kustomization.yaml")); err != nil { + return fmt.Errorf("generate kustomization failed: %w", err) + } + + if err := os.MkdirAll(path.Join(base, "roles"), os.ModePerm); err != nil { + return fmt.Errorf("generate roles failed: %w", err) + } + + if err := execTemplate(options, kustomizationRolesTmpl, path.Join(base, "roles/kustomization.yaml")); err != nil { + return fmt.Errorf("generate roles kustomization failed: %w", err) + } + + if err := copyFile(filepath.Join(base, "rbac.yaml"), filepath.Join(base, "roles/rbac.yaml")); err != nil { + return fmt.Errorf("generate rbac failed: %w", err) + } + return nil +} + +func build(base, output 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(output, resources); err != nil { + return err + } + + return nil +} diff --git a/pkg/install/options.go b/pkg/install/options.go new file mode 100644 index 00000000..a15410d3 --- /dev/null +++ b/pkg/install/options.go @@ -0,0 +1,64 @@ +/* +Copyright 2020 The Flux CD contributors. + +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 install + +import "time" + +type Options struct { + BaseURL string + Version string + Namespace string + Components []string + EventsAddr string + Registry string + ImagePullSecret string + Arch string + WatchAllNamespaces bool + NetworkPolicy bool + LogLevel string + NotificationController string + ManifestsFile string + Timeout time.Duration +} + +func MakeDefaultOptions() Options { + return Options{ + Version: "latest", + Namespace: "gotk-system", + Components: []string{"source-controller", "kustomize-controller", "helm-controller", "notification-controller"}, + EventsAddr: "", + Registry: "ghcr.io/fluxcd", + ImagePullSecret: "", + Arch: "amd64", + WatchAllNamespaces: true, + NetworkPolicy: true, + LogLevel: "info", + BaseURL: "https://github.com/fluxcd/toolkit/releases", + NotificationController: "notification-controller", + ManifestsFile: "toolkit-components.yaml", + Timeout: time.Minute, + } +} + +func containsItemString(s []string, e string) bool { + for _, a := range s { + if a == e { + return true + } + } + return false +} diff --git a/pkg/install/templates.go b/pkg/install/templates.go new file mode 100644 index 00000000..4cc1b357 --- /dev/null +++ b/pkg/install/templates.go @@ -0,0 +1,195 @@ +/* +Copyright 2020 The Flux CD contributors. + +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 install + +import ( + "bufio" + "bytes" + "io" + "os" + "text/template" +) + +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 }} +` + +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 namespaceTmpl = `--- +apiVersion: v1 +kind: Namespace +metadata: + name: {{.Namespace}} +` + +func execTemplate(obj interface{}, tmpl, filename string) error { + t, err := template.New("tmpl").Parse(tmpl) + if err != nil { + return err + } + + var data bytes.Buffer + writer := bufio.NewWriter(&data) + if err := t.Execute(writer, obj); err != nil { + return err + } + + if err := writer.Flush(); err != nil { + return err + } + + file, err := os.Create(filename) + if err != nil { + return err + } + defer file.Close() + + _, err = io.WriteString(file, data.String()) + if err != nil { + return err + } + + return file.Sync() +} + +func copyFile(src, dst string) error { + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + + out, err := os.Create(dst) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, in) + if err != nil { + return err + } + return out.Close() +} From 567264627878d9f1acc03ab2069e462da820c57e Mon Sep 17 00:00:00 2001 From: Stefan Prodan Date: Thu, 8 Oct 2020 12:30:43 +0300 Subject: [PATCH 2/2] 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)) }