From 6003d111569496eb0aea8d889636d7c6dd6387ed Mon Sep 17 00:00:00 2001 From: Stefan Prodan Date: Thu, 25 Feb 2021 11:17:05 +0200 Subject: [PATCH] Embed the install manifests in flux binary - add make target for generating the install manifests using kustomize - embed the generated manifests in flux binary - the install and bootstrap commands default to using the embedded manifests - download the install manifests from GitHub only if the install/bootstrap version arg is set Signed-off-by: Stefan Prodan --- .github/workflows/bootstrap.yaml | 22 ++-- .github/workflows/e2e.yaml | 91 +++++++++-------- .github/workflows/release.yaml | 36 +------ .github/workflows/scan.yaml | 5 + .gitignore | 3 +- Makefile | 5 +- cmd/flux/bootstrap.go | 29 +++--- cmd/flux/embed.go | 31 ++++++ cmd/flux/install.go | 130 +++++++++++++----------- cmd/flux/main.go | 4 +- cmd/flux/version.go | 42 ++++++++ docs/cmd/flux_bootstrap.md | 2 +- docs/cmd/flux_bootstrap_github.md | 2 +- docs/cmd/flux_bootstrap_gitlab.md | 2 +- docs/cmd/flux_install.md | 2 +- manifests/scripts/bundle.sh | 70 +++++++++++++ pkg/manifestgen/install/install.go | 32 ++++-- pkg/manifestgen/install/install_test.go | 2 +- 18 files changed, 327 insertions(+), 183 deletions(-) create mode 100644 cmd/flux/embed.go create mode 100644 cmd/flux/version.go create mode 100755 manifests/scripts/bundle.sh diff --git a/.github/workflows/bootstrap.yaml b/.github/workflows/bootstrap.yaml index d65ee2a2..41d569a6 100644 --- a/.github/workflows/bootstrap.yaml +++ b/.github/workflows/bootstrap.yaml @@ -17,23 +17,27 @@ jobs: uses: actions/cache@v1 with: path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + key: ${{ runner.os }}-go1.16-${{ hashFiles('**/go.sum') }} restore-keys: | - ${{ runner.os }}-go- + ${{ runner.os }}-go1.16- - name: Setup Go uses: actions/setup-go@v2 with: go-version: 1.16.x - name: Setup Kubernetes uses: engineerd/setup-kind@v0.5.0 + - name: Setup Kustomize + uses: fluxcd/pkg//actions/kustomize@main + - name: Build + run: | + make build-manifests + go build -o /tmp/flux ./cmd/flux - name: Set outputs id: vars run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" - - name: Build - run: sudo go build -o ./bin/flux ./cmd/flux - name: bootstrap init run: | - ./bin/flux bootstrap github --manifests ./manifests/install/ \ + /tmp/flux bootstrap github --manifests ./manifests/install/ \ --owner=fluxcd-testing \ --repository=flux-test-${{ steps.vars.outputs.sha_short }} \ --branch=main \ @@ -42,7 +46,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }} - name: bootstrap no-op run: | - ./bin/flux bootstrap github --manifests ./manifests/install/ \ + /tmp/flux bootstrap github --manifests ./manifests/install/ \ --owner=fluxcd-testing \ --repository=flux-test-${{ steps.vars.outputs.sha_short }} \ --branch=main \ @@ -51,11 +55,11 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }} - name: uninstall run: | - ./bin/flux uninstall -s --keep-namespace + /tmp/flux uninstall -s --keep-namespace kubectl delete ns flux-system --timeout=10m --wait=true - name: bootstrap reinstall run: | - ./bin/flux bootstrap github --manifests ./manifests/install/ \ + /tmp/flux bootstrap github --manifests ./manifests/install/ \ --owner=fluxcd-testing \ --repository=flux-test-${{ steps.vars.outputs.sha_short }} \ --branch=main \ @@ -64,7 +68,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }} - name: delete repository run: | - ./bin/flux bootstrap github --manifests ./manifests/install/ \ + /tmp/flux bootstrap github --manifests ./manifests/install/ \ --owner=fluxcd-testing \ --repository=flux-test-${{ steps.vars.outputs.sha_short }} \ --branch=main \ diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 70d8c2e3..088be0ea 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -16,9 +16,9 @@ jobs: uses: actions/cache@v1 with: path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + key: ${{ runner.os }}-go1.16-${{ hashFiles('**/go.sum') }} restore-keys: | - ${{ runner.os }}-go- + ${{ runner.os }}-go1.16- - name: Setup Go uses: actions/setup-go@v2 with: @@ -33,6 +33,8 @@ jobs: run: | kubectl apply -f https://docs.projectcalico.org/v3.16/manifests/calico.yaml kubectl -n kube-system set env daemonset/calico-node FELIX_IGNORELOOSERPF=true + - name: Setup Kustomize + uses: fluxcd/pkg//actions/kustomize@main - name: Run test run: make test - name: Check if working tree is dirty @@ -43,43 +45,44 @@ jobs: exit 1 fi - name: Build - run: sudo go build -o ./bin/flux ./cmd/flux + run: | + go build -o /tmp/flux ./cmd/flux - name: flux check --pre run: | - ./bin/flux check --pre + /tmp/flux check --pre - name: flux install --manifests run: | - ./bin/flux install --manifests ./manifests/install/ + /tmp/flux install --manifests ./manifests/install/ - name: flux create secret run: | - ./bin/flux create secret git git-ssh-test \ + /tmp/flux create secret git git-ssh-test \ --url ssh://git@github.com/stefanprodan/podinfo - ./bin/flux create secret git git-https-test \ + /tmp/flux create secret git git-https-test \ --url https://github.com/stefanprodan/podinfo \ --username=test --password=test - ./bin/flux create secret helm helm-test \ + /tmp/flux create secret helm helm-test \ --username=test --password=test - name: flux create source git run: | - ./bin/flux create source git podinfo \ + /tmp/flux create source git podinfo \ --url https://github.com/stefanprodan/podinfo \ --tag-semver=">=3.2.3" - name: flux create source git export apply run: | - ./bin/flux create source git podinfo-export \ + /tmp/flux create source git podinfo-export \ --url https://github.com/stefanprodan/podinfo \ --tag-semver=">=3.2.3" \ --export | kubectl apply -f - - ./bin/flux delete source git podinfo-export --silent + /tmp/flux delete source git podinfo-export --silent - name: flux get sources git run: | - ./bin/flux get sources git + /tmp/flux get sources git - name: flux get sources git --all-namespaces run: | - ./bin/flux get sources git --all-namespaces + /tmp/flux get sources git --all-namespaces - name: flux create kustomization run: | - ./bin/flux create kustomization podinfo \ + /tmp/flux create kustomization podinfo \ --source=podinfo \ --path="./deploy/overlays/dev" \ --prune=true \ @@ -90,112 +93,112 @@ jobs: --health-check-timeout=3m - name: flux reconcile kustomization --with-source run: | - ./bin/flux reconcile kustomization podinfo --with-source + /tmp/flux reconcile kustomization podinfo --with-source - name: flux get kustomizations run: | - ./bin/flux get kustomizations + /tmp/flux get kustomizations - name: flux get kustomizations --all-namespaces run: | - ./bin/flux get kustomizations --all-namespaces + /tmp/flux get kustomizations --all-namespaces - name: flux suspend kustomization run: | - ./bin/flux suspend kustomization podinfo + /tmp/flux suspend kustomization podinfo - name: flux resume kustomization run: | - ./bin/flux resume kustomization podinfo + /tmp/flux resume kustomization podinfo - name: flux export run: | - ./bin/flux export source git --all - ./bin/flux export kustomization --all + /tmp/flux export source git --all + /tmp/flux export kustomization --all - name: flux delete kustomization run: | - ./bin/flux delete kustomization podinfo --silent + /tmp/flux delete kustomization podinfo --silent - name: flux create source helm run: | - ./bin/flux create source helm podinfo \ + /tmp/flux create source helm podinfo \ --url https://stefanprodan.github.io/podinfo - name: flux create helmrelease --source=HelmRepository/podinfo run: | - ./bin/flux create hr podinfo-helm \ + /tmp/flux create hr podinfo-helm \ --target-namespace=default \ --source=HelmRepository/podinfo \ --chart=podinfo \ --chart-version=">4.0.0 <5.0.0" - name: flux create helmrelease --source=GitRepository/podinfo run: | - ./bin/flux create hr podinfo-git \ + /tmp/flux create hr podinfo-git \ --target-namespace=default \ --source=GitRepository/podinfo \ --chart=./charts/podinfo - name: flux reconcile helmrelease --with-source run: | - ./bin/flux reconcile helmrelease podinfo-git --with-source + /tmp/flux reconcile helmrelease podinfo-git --with-source - name: flux get helmreleases run: | - ./bin/flux get helmreleases + /tmp/flux get helmreleases - name: flux get helmreleases --all-namespaces run: | - ./bin/flux get helmreleases --all-namespaces + /tmp/flux get helmreleases --all-namespaces - name: flux export helmrelease run: | - ./bin/flux export hr --all + /tmp/flux export hr --all - name: flux delete helmrelease podinfo-helm run: | - ./bin/flux delete hr podinfo-helm --silent + /tmp/flux delete hr podinfo-helm --silent - name: flux delete helmrelease podinfo-git run: | - ./bin/flux delete hr podinfo-git --silent + /tmp/flux delete hr podinfo-git --silent - name: flux delete source helm run: | - ./bin/flux delete source helm podinfo --silent + /tmp/flux delete source helm podinfo --silent - name: flux delete source git run: | - ./bin/flux delete source git podinfo --silent + /tmp/flux delete source git podinfo --silent - name: flux create tenant run: | - ./bin/flux create tenant dev-team --with-namespace=apps - ./bin/flux -n apps create source helm podinfo \ + /tmp/flux create tenant dev-team --with-namespace=apps + /tmp/flux -n apps create source helm podinfo \ --url https://stefanprodan.github.io/podinfo - ./bin/flux -n apps create hr podinfo-helm \ + /tmp/flux -n apps create hr podinfo-helm \ --source=HelmRepository/podinfo \ --chart=podinfo \ --chart-version="5.0.x" \ --service-account=dev-team - name: flux create image repository run: | - ./bin/flux create image repository podinfo \ + /tmp/flux create image repository podinfo \ --image=ghcr.io/stefanprodan/podinfo \ --interval=1m - name: flux create image policy run: | - ./bin/flux create image policy podinfo \ + /tmp/flux create image policy podinfo \ --image-ref=podinfo \ --interval=1m \ --select-semver=5.0.x - name: flux create image policy podinfo-select-alpha run: | - ./bin/flux create image policy podinfo-alpha \ + /tmp/flux create image policy podinfo-alpha \ --image-ref=podinfo \ --interval=1m \ --select-alpha=desc - name: flux get image policy run: | - ./bin/flux get image policy podinfo | grep '5.0.3' + /tmp/flux get image policy podinfo | grep '5.0.3' - name: flux2-kustomize-helm-example run: | - ./bin/flux create source git flux-system \ + /tmp/flux create source git flux-system \ --url=https://github.com/fluxcd/flux2-kustomize-helm-example \ --branch=main - ./bin/flux create kustomization flux-system \ + /tmp/flux create kustomization flux-system \ --source=flux-system \ --path=./clusters/staging kubectl -n flux-system wait kustomization/apps --for=condition=ready --timeout=2m - name: flux check run: | - ./bin/flux check + /tmp/flux check - name: flux uninstall run: | - ./bin/flux uninstall --silent + /tmp/flux uninstall --silent - name: Debug failure if: failure() run: | diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 6ac8ee84..8a911d12 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -2,7 +2,7 @@ name: release on: push: - tags: [ '*' ] + tags: [ 'v*' ] jobs: goreleaser: @@ -28,38 +28,10 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Setup Kustomize uses: fluxcd/pkg//actions/kustomize@main - - name: Generate manifests tarball - run: | - mkdir -p ./output - files="" - - # build controllers - for controller in ./manifests/bases/*/; do - output_path="./output/$(basename $controller).yaml" - echo "building $controller to $output_path" - - kustomize build $controller > $output_path - files+=" $(basename $output_path)" - done - - # build rbac - rbac_path="./manifests/rbac" - rbac_output_path="./output/rbac.yaml" - echo "building $rbac_path to $rbac_output_path" - kustomize build $rbac_path > $rbac_output_path - files+=" $(basename $rbac_output_path)" - - # build policies - policies_path="./manifests/policies" - policies_output_path="./output/policies.yaml" - echo "building $policies_path to $policies_output_path" - kustomize build $policies_path > $policies_output_path - files+=" $(basename $policies_output_path)" - - # create tarball - cd ./output && tar -cvzf manifests.tar.gz $files - - name: Generate install manifest + - name: Generate manifests run: | + make build-manifests + ./manifests/scripts/bundle.sh ./output manifests.tar.gz kustomize build ./manifests/install > ./output/install.yaml - name: Run GoReleaser uses: goreleaser/goreleaser-action@v1 diff --git a/.github/workflows/scan.yaml b/.github/workflows/scan.yaml index a949ab30..b020c509 100644 --- a/.github/workflows/scan.yaml +++ b/.github/workflows/scan.yaml @@ -27,6 +27,11 @@ jobs: if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository steps: - uses: actions/checkout@v2 + - name: Setup Kustomize + uses: fluxcd/pkg//actions/kustomize@main + - name: Build manifests + run: | + make build-manifests - name: Run Snyk to check for vulnerabilities uses: snyk/actions/golang@master continue-on-error: true diff --git a/.gitignore b/.gitignore index d6ce0835..2966ab63 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,5 @@ # Dependency directories (remove the comment below to include it) # vendor/ bin/ -output/ \ No newline at end of file +output/ +cmd/flux/manifests/ diff --git a/Makefile b/Makefile index 3d4765bd..ed6333f3 100644 --- a/Makefile +++ b/Makefile @@ -11,9 +11,12 @@ fmt: vet: go vet ./... -test: tidy fmt vet docs +test: build-manifests tidy fmt vet docs go test ./... -coverprofile cover.out +build-manifests: + ./manifests/scripts/bundle.sh + build: CGO_ENABLED=0 go build -o ./bin/flux ./cmd/flux diff --git a/cmd/flux/bootstrap.go b/cmd/flux/bootstrap.go index b90e7160..3243e629 100644 --- a/cmd/flux/bootstrap.go +++ b/cmd/flux/bootstrap.go @@ -70,8 +70,8 @@ const ( var bootstrapArgs = NewBootstrapFlags() func init() { - bootstrapCmd.PersistentFlags().StringVarP(&bootstrapArgs.version, "version", "v", rootArgs.defaults.Version, - "toolkit version") + bootstrapCmd.PersistentFlags().StringVarP(&bootstrapArgs.version, "version", "v", "", + "toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases") bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.defaultComponents, "components", rootArgs.defaults.Components, "list of components, accepts comma-separated values") bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.extraComponents, "components-extra", nil, @@ -126,23 +126,18 @@ func bootstrapValidate() error { } func generateInstallManifests(targetPath, namespace, tmpDir string, localManifests string) (string, error) { - if bootstrapArgs.version == install.MakeDefaultOptions().Version { - version, err := install.GetLatestVersion() - if err != nil { - return "", err - } - bootstrapArgs.version = version + if ver, err := getVersion(bootstrapArgs.version); err != nil { + return "", err } else { - if ok, err := install.ExistingVersion(bootstrapArgs.version); err != nil || !ok { - if err == nil { - err = fmt.Errorf("targeted version '%s' does not exist", bootstrapArgs.version) - } - return "", err - } + bootstrapArgs.version = ver } - if !utils.CompatibleVersion(VERSION, bootstrapArgs.version) { - return "", fmt.Errorf("targeted version '%s' is not compatible with your current version of flux (%s)", bootstrapArgs.version, VERSION) + manifestsBase := "" + if isEmbeddedVersion(bootstrapArgs.version) { + if err := writeEmbeddedManifests(tmpDir); err != nil { + return "", err + } + manifestsBase = tmpDir } opts := install.Options{ @@ -167,7 +162,7 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes opts.BaseURL = rootArgs.defaults.BaseURL } - output, err := install.Generate(opts) + output, err := install.Generate(opts, manifestsBase) if err != nil { return "", fmt.Errorf("generating install manifests failed: %w", err) } diff --git a/cmd/flux/embed.go b/cmd/flux/embed.go new file mode 100644 index 00000000..3e5142ea --- /dev/null +++ b/cmd/flux/embed.go @@ -0,0 +1,31 @@ +package main + +import ( + "embed" + "fmt" + "io/fs" + "os" + "path" +) + +//go:embed manifests/*.yaml +var embeddedManifests embed.FS + +func writeEmbeddedManifests(dir string) error { + manifests, err := fs.ReadDir(embeddedManifests, "manifests") + if err != nil { + return err + } + for _, manifest := range manifests { + data, err := fs.ReadFile(embeddedManifests, path.Join("manifests", manifest.Name())) + if err != nil { + return fmt.Errorf("reading file failed: %w", err) + } + + err = os.WriteFile(path.Join(dir, manifest.Name()), data, 0666) + if err != nil { + return fmt.Errorf("writing file failed: %w", err) + } + } + return nil +} diff --git a/cmd/flux/install.go b/cmd/flux/install.go index bac6d058..fb2740e2 100644 --- a/cmd/flux/install.go +++ b/cmd/flux/install.go @@ -55,79 +55,81 @@ If a previous version is installed, then an in-place upgrade will be performed.` RunE: installCmdRun, } -var ( - installExport bool - installDryRun bool - installManifestsPath string - installVersion string - installDefaultComponents []string - installExtraComponents []string - installRegistry string - installImagePullSecret string - installWatchAllNamespaces bool - installNetworkPolicy bool - installArch flags.Arch - installLogLevel = flags.LogLevel(rootArgs.defaults.LogLevel) - installClusterDomain string - installTolerationKeys []string -) +type installFlags struct { + export bool + dryRun bool + version string + defaultComponents []string + extraComponents []string + registry string + imagePullSecret string + branch string + watchAllNamespaces bool + networkPolicy bool + manifestsPath string + arch flags.Arch + logLevel flags.LogLevel + tokenAuth bool + clusterDomain string + tolerationKeys []string +} + +var installArgs = NewInstallFlags() func init() { - installCmd.Flags().BoolVar(&installExport, "export", false, + installCmd.Flags().BoolVar(&installArgs.export, "export", false, "write the install manifests to stdout and exit") - installCmd.Flags().BoolVarP(&installDryRun, "dry-run", "", false, + installCmd.Flags().BoolVarP(&installArgs.dryRun, "dry-run", "", false, "only print the object that would be applied") - installCmd.Flags().StringVarP(&installVersion, "version", "v", rootArgs.defaults.Version, - "toolkit version") - installCmd.Flags().StringSliceVar(&installDefaultComponents, "components", rootArgs.defaults.Components, + installCmd.Flags().StringVarP(&installArgs.version, "version", "v", "", + "toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases") + installCmd.Flags().StringSliceVar(&installArgs.defaultComponents, "components", rootArgs.defaults.Components, "list of components, accepts comma-separated values") - installCmd.Flags().StringSliceVar(&installExtraComponents, "components-extra", nil, + installCmd.Flags().StringSliceVar(&installArgs.extraComponents, "components-extra", nil, "list of components in addition to those supplied or defaulted, accepts comma-separated values") - installCmd.Flags().StringVar(&installManifestsPath, "manifests", "", "path to the manifest directory") - installCmd.Flags().StringVar(&installRegistry, "registry", rootArgs.defaults.Registry, + installCmd.Flags().StringVar(&installArgs.manifestsPath, "manifests", "", "path to the manifest directory") + installCmd.Flags().StringVar(&installArgs.registry, "registry", rootArgs.defaults.Registry, "container registry where the toolkit images are published") - installCmd.Flags().StringVar(&installImagePullSecret, "image-pull-secret", "", + installCmd.Flags().StringVar(&installArgs.imagePullSecret, "image-pull-secret", "", "Kubernetes secret name used for pulling the toolkit images from a private registry") - installCmd.Flags().Var(&installArch, "arch", installArch.Description()) - installCmd.Flags().BoolVar(&installWatchAllNamespaces, "watch-all-namespaces", rootArgs.defaults.WatchAllNamespaces, + installCmd.Flags().Var(&installArgs.arch, "arch", installArgs.arch.Description()) + installCmd.Flags().BoolVar(&installArgs.watchAllNamespaces, "watch-all-namespaces", rootArgs.defaults.WatchAllNamespaces, "watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed") - installCmd.Flags().Var(&installLogLevel, "log-level", installLogLevel.Description()) - installCmd.Flags().BoolVar(&installNetworkPolicy, "network-policy", rootArgs.defaults.NetworkPolicy, + installCmd.Flags().Var(&installArgs.logLevel, "log-level", installArgs.logLevel.Description()) + installCmd.Flags().BoolVar(&installArgs.networkPolicy, "network-policy", rootArgs.defaults.NetworkPolicy, "deny ingress access to the toolkit controllers from other namespaces using network policies") - installCmd.Flags().StringVar(&installClusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain") - installCmd.Flags().StringSliceVar(&installTolerationKeys, "toleration-keys", nil, + installCmd.Flags().StringVar(&installArgs.clusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain") + installCmd.Flags().StringSliceVar(&installArgs.tolerationKeys, "toleration-keys", nil, "list of toleration keys used to schedule the components pods onto nodes with matching taints") installCmd.Flags().MarkHidden("manifests") installCmd.Flags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64") rootCmd.AddCommand(installCmd) } +func NewInstallFlags() installFlags { + return installFlags{ + logLevel: flags.LogLevel(rootArgs.defaults.LogLevel), + } +} + func installCmdRun(cmd *cobra.Command, args []string) error { ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) defer cancel() - components := append(installDefaultComponents, installExtraComponents...) + components := append(installArgs.defaultComponents, installArgs.extraComponents...) err := utils.ValidateComponents(components) if err != nil { return err } - if installVersion == install.MakeDefaultOptions().Version { - installVersion, err = install.GetLatestVersion() - if err != nil { - return err - } + if ver, err := getVersion(installArgs.version); err != nil { + return err } else { - if ok, err := install.ExistingVersion(installVersion); err != nil || !ok { - if err == nil { - err = fmt.Errorf("targeted version '%s' does not exist", installVersion) - } - return err - } + installArgs.version = ver } - if !utils.CompatibleVersion(VERSION, installVersion) { - return fmt.Errorf("targeted version '%s' is not compatible with your current version of flux (%s)", installVersion, VERSION) + if !installArgs.export { + logger.Generatef("generating manifests") } tmpDir, err := ioutil.TempDir("", rootArgs.namespace) @@ -136,32 +138,36 @@ func installCmdRun(cmd *cobra.Command, args []string) error { } defer os.RemoveAll(tmpDir) - if !installExport { - logger.Generatef("generating manifests") + manifestsBase := "" + if isEmbeddedVersion(installArgs.version) { + if err := writeEmbeddedManifests(tmpDir); err != nil { + return err + } + manifestsBase = tmpDir } opts := install.Options{ - BaseURL: installManifestsPath, - Version: installVersion, + BaseURL: installArgs.manifestsPath, + Version: installArgs.version, Namespace: rootArgs.namespace, Components: components, - Registry: installRegistry, - ImagePullSecret: installImagePullSecret, - WatchAllNamespaces: installWatchAllNamespaces, - NetworkPolicy: installNetworkPolicy, - LogLevel: installLogLevel.String(), + Registry: installArgs.registry, + ImagePullSecret: installArgs.imagePullSecret, + WatchAllNamespaces: installArgs.watchAllNamespaces, + NetworkPolicy: installArgs.networkPolicy, + LogLevel: installArgs.logLevel.String(), NotificationController: rootArgs.defaults.NotificationController, ManifestFile: fmt.Sprintf("%s.yaml", rootArgs.namespace), Timeout: rootArgs.timeout, - ClusterDomain: installClusterDomain, - TolerationKeys: installTolerationKeys, + ClusterDomain: installArgs.clusterDomain, + TolerationKeys: installArgs.tolerationKeys, } - if installManifestsPath == "" { + if installArgs.manifestsPath == "" { opts.BaseURL = install.MakeDefaultOptions().BaseURL } - manifest, err := install.Generate(opts) + manifest, err := install.Generate(opts, manifestsBase) if err != nil { return fmt.Errorf("install failed: %w", err) } @@ -172,9 +178,9 @@ func installCmdRun(cmd *cobra.Command, args []string) error { if rootArgs.verbose { fmt.Print(manifest.Content) - } else if installExport { + } else if installArgs.export { fmt.Println("---") - fmt.Println("# Flux version:", installVersion) + fmt.Println("# Flux version:", installArgs.version) fmt.Println("# Components:", strings.Join(components, ",")) fmt.Print(manifest.Content) fmt.Println("---") @@ -189,7 +195,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error { } kubectlArgs := []string{"apply", "-f", filepath.Join(tmpDir, manifest.Path)} - if installDryRun { + if installArgs.dryRun { kubectlArgs = append(kubectlArgs, "--dry-run=client") applyOutput = utils.ModeOS } @@ -197,7 +203,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error { return fmt.Errorf("install failed") } - if installDryRun { + if installArgs.dryRun { logger.Successf("install dry-run finished") return nil } diff --git a/cmd/flux/main.go b/cmd/flux/main.go index d4410338..ecd75a90 100644 --- a/cmd/flux/main.go +++ b/cmd/flux/main.go @@ -115,10 +115,12 @@ func init() { } func NewRootFlags() rootFlags { - return rootFlags{ + rf := rootFlags{ pollInterval: 2 * time.Second, defaults: install.MakeDefaultOptions(), } + rf.defaults.Version = "v" + VERSION + return rf } func main() { diff --git a/cmd/flux/version.go b/cmd/flux/version.go new file mode 100644 index 00000000..cbbcf2b6 --- /dev/null +++ b/cmd/flux/version.go @@ -0,0 +1,42 @@ +package main + +import ( + "fmt" + + "github.com/fluxcd/flux2/internal/utils" + "github.com/fluxcd/flux2/pkg/manifestgen/install" +) + +func getVersion(input string) (string, error) { + if input == "" { + return rootArgs.defaults.Version, nil + } + + if isEmbeddedVersion(input) { + return input, nil + } + + var err error + if input == install.MakeDefaultOptions().Version { + input, err = install.GetLatestVersion() + if err != nil { + return "", err + } + } else { + if ok, err := install.ExistingVersion(input); err != nil || !ok { + if err == nil { + err = fmt.Errorf("targeted version '%s' does not exist", input) + } + return "", err + } + } + + if !utils.CompatibleVersion(VERSION, input) { + return "", fmt.Errorf("targeted version '%s' is not compatible with your current version of flux (%s)", input, VERSION) + } + return input, nil +} + +func isEmbeddedVersion(input string) bool { + return input == rootArgs.defaults.Version +} diff --git a/docs/cmd/flux_bootstrap.md b/docs/cmd/flux_bootstrap.md index 6ee474cb..530a9c3e 100644 --- a/docs/cmd/flux_bootstrap.md +++ b/docs/cmd/flux_bootstrap.md @@ -20,7 +20,7 @@ The bootstrap sub-commands bootstrap the toolkit components on the targeted Git --registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd") --token-auth when enabled, the personal access token will be used instead of SSH deploy key --toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints - -v, --version string toolkit version (default "latest") + -v, --version string toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases --watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true) ``` diff --git a/docs/cmd/flux_bootstrap_github.md b/docs/cmd/flux_bootstrap_github.md index 94430565..eca8d117 100644 --- a/docs/cmd/flux_bootstrap_github.md +++ b/docs/cmd/flux_bootstrap_github.md @@ -76,7 +76,7 @@ flux bootstrap github [flags] --token-auth when enabled, the personal access token will be used instead of SSH deploy key --toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints --verbose print generated objects - -v, --version string toolkit version (default "latest") + -v, --version string toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases --watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true) ``` diff --git a/docs/cmd/flux_bootstrap_gitlab.md b/docs/cmd/flux_bootstrap_gitlab.md index 498b4c06..027201e5 100644 --- a/docs/cmd/flux_bootstrap_gitlab.md +++ b/docs/cmd/flux_bootstrap_gitlab.md @@ -72,7 +72,7 @@ flux bootstrap gitlab [flags] --token-auth when enabled, the personal access token will be used instead of SSH deploy key --toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints --verbose print generated objects - -v, --version string toolkit version (default "latest") + -v, --version string toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases --watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true) ``` diff --git a/docs/cmd/flux_install.md b/docs/cmd/flux_install.md index faad5878..8fe68083 100644 --- a/docs/cmd/flux_install.md +++ b/docs/cmd/flux_install.md @@ -45,7 +45,7 @@ flux install [flags] --network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true) --registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd") --toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints - -v, --version string toolkit version (default "latest") + -v, --version string toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases --watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true) ``` diff --git a/manifests/scripts/bundle.sh b/manifests/scripts/bundle.sh new file mode 100755 index 00000000..b5f89607 --- /dev/null +++ b/manifests/scripts/bundle.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +# Copyright 2020 The Flux authors. All rights reserved. +# +# 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. + +set -e + +REPO_ROOT=$(git rev-parse --show-toplevel) +OUT_PATH=${1:-"${REPO_ROOT}/cmd/flux/manifests"} +TAR=${2} + +info() { + echo '[INFO] ' "$@" +} + +fatal() { + echo '[ERROR] ' "$@" >&2 + exit 1 +} + +build() { + info "building $(basename $2)" + kustomize build "$1" > "$2" +} + +if ! [ -x "$(command -v kustomize)" ]; then + fatal 'kustomize is not installed' +fi + +rm -rf $OUT_PATH +mkdir -p $OUT_PATH +files="" + +info using "$(kustomize version --short)" + +# build controllers +for controller in ${REPO_ROOT}/manifests/bases/*/; do + output_path="${OUT_PATH}/$(basename $controller).yaml" + build $controller $output_path + files+=" $(basename $output_path)" +done + +# build rbac +rbac_path="${REPO_ROOT}/manifests/rbac" +rbac_output_path="${OUT_PATH}/rbac.yaml" +build $rbac_path $rbac_output_path +files+=" $(basename $rbac_output_path)" + +# build policies +policies_path="${REPO_ROOT}/manifests/policies" +policies_output_path="${OUT_PATH}/policies.yaml" +build $policies_path $policies_output_path +files+=" $(basename $policies_output_path)" + +# create tarball +if [[ -n $TAR ]];then + info "archiving $TAR" + cd ${OUT_PATH} && tar -czf $TAR $files +fi diff --git a/pkg/manifestgen/install/install.go b/pkg/manifestgen/install/install.go index f5168545..e1b84037 100644 --- a/pkg/manifestgen/install/install.go +++ b/pkg/manifestgen/install/install.go @@ -35,17 +35,15 @@ 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) (*manifestgen.Manifest, error) { +// The manifestsBase should be set to an empty string when Generate is +// called by consumers that don't embed the manifests. +func Generate(options Options, manifestsBase string) (*manifestgen.Manifest, error) { ctx, cancel := context.WithTimeout(context.Background(), options.Timeout) defer cancel() - tmpDir, err := ioutil.TempDir("", options.Namespace) - if err != nil { - return nil, fmt.Errorf("temp dir error: %w", err) - } - defer os.RemoveAll(tmpDir) + var err error - output, err := securejoin.SecureJoin(tmpDir, options.ManifestFile) + output, err := securejoin.SecureJoin(manifestsBase, options.ManifestFile) if err != nil { return nil, err } @@ -55,15 +53,27 @@ func Generate(options Options) (*manifestgen.Manifest, error) { return nil, err } } else { - if err := fetch(ctx, options.BaseURL, options.Version, tmpDir); err != nil { - return nil, err + // download the manifests base from GitHub + if manifestsBase == "" { + manifestsBase, err = ioutil.TempDir("", options.Namespace) + if err != nil { + return nil, fmt.Errorf("temp dir error: %w", err) + } + defer os.RemoveAll(manifestsBase) + output, err = securejoin.SecureJoin(manifestsBase, options.ManifestFile) + if err != nil { + return nil, err + } + if err := fetch(ctx, options.BaseURL, options.Version, manifestsBase); err != nil { + return nil, err + } } - if err := generate(tmpDir, options); err != nil { + if err := generate(manifestsBase, options); err != nil { return nil, err } - if err := build(tmpDir, output); err != nil { + if err := build(manifestsBase, output); err != nil { return nil, err } } diff --git a/pkg/manifestgen/install/install_test.go b/pkg/manifestgen/install/install_test.go index 834b26d4..eab771ad 100644 --- a/pkg/manifestgen/install/install_test.go +++ b/pkg/manifestgen/install/install_test.go @@ -25,7 +25,7 @@ import ( func TestGenerate(t *testing.T) { opts := MakeDefaultOptions() opts.TolerationKeys = []string{"node.kubernetes.io/controllers"} - output, err := Generate(opts) + output, err := Generate(opts, "") if err != nil { t.Fatal(err) }