From 07d7673903e60ddefbace53ca84da93ed604a135 Mon Sep 17 00:00:00 2001 From: yiannis Date: Tue, 31 Oct 2023 14:15:05 +0000 Subject: [PATCH] fix: Remove GitOps Run CLI commands --- cmd/gitops/beta/cmd.go | 18 - cmd/gitops/beta/run/cleanup.go | 62 - cmd/gitops/beta/run/cleanup_test.go | 87 -- cmd/gitops/beta/run/cmd.go | 1338 ------------------ cmd/gitops/beta/run/trim_k8s_version.go | 14 - cmd/gitops/beta/run/trim_k9s_version_test.go | 79 -- cmd/gitops/remove/cmd.go | 18 - cmd/gitops/remove/run/cmd.go | 202 --- cmd/gitops/root/cmd.go | 16 +- 9 files changed, 4 insertions(+), 1830 deletions(-) delete mode 100644 cmd/gitops/beta/cmd.go delete mode 100644 cmd/gitops/beta/run/cleanup.go delete mode 100644 cmd/gitops/beta/run/cleanup_test.go delete mode 100644 cmd/gitops/beta/run/cmd.go delete mode 100644 cmd/gitops/beta/run/trim_k8s_version.go delete mode 100644 cmd/gitops/beta/run/trim_k9s_version_test.go delete mode 100644 cmd/gitops/remove/cmd.go delete mode 100644 cmd/gitops/remove/run/cmd.go diff --git a/cmd/gitops/beta/cmd.go b/cmd/gitops/beta/cmd.go deleted file mode 100644 index 9edb04cea2..0000000000 --- a/cmd/gitops/beta/cmd.go +++ /dev/null @@ -1,18 +0,0 @@ -package beta - -import ( - "github.com/spf13/cobra" - "github.com/weaveworks/weave-gitops/cmd/gitops/beta/run" - "github.com/weaveworks/weave-gitops/cmd/gitops/config" -) - -func GetCommand(opts *config.Options) *cobra.Command { - cmd := &cobra.Command{ - Use: "beta", - Short: "This component contains unstable or still-in-development functionality", - } - - cmd.AddCommand(run.RunCommand(opts)) - - return cmd -} diff --git a/cmd/gitops/beta/run/cleanup.go b/cmd/gitops/beta/run/cleanup.go deleted file mode 100644 index 3c6ed21d0d..0000000000 --- a/cmd/gitops/beta/run/cleanup.go +++ /dev/null @@ -1,62 +0,0 @@ -package run - -import ( - "context" - "errors" - "fmt" - - "github.com/weaveworks/weave-gitops/pkg/logger" -) - -// CleanupFunc is a function supposed to be called while a GitOps Run session -// is terminating. Each component creating resources on the cluster should -// return such a function that is then added to the CleanupFuncs stack by -// the orchestrating code and removed from it and executed during shutdown. -type CleanupFunc func(ctx context.Context, log logger.Logger) error - -// CleanupFuncs is a stack holding CleanupFunc references that are used -// to roll up all resources created during an GitOps Run session as soon -// as the session is terminated. -type CleanupFuncs struct { - fns []CleanupFunc -} - -// Push implements the stack's Push operation, adding the given CleanupFunc -// to the top of the stack. -func (c *CleanupFuncs) Push(f CleanupFunc) { - if f == nil { - return - } - - c.fns = append(c.fns, f) -} - -var ErrEmptyStack = errors.New("stack is empty") - -// Pop implements the stack's Pop operation, returning and removing -// the top CleanupFunc from the stack. -func (c *CleanupFuncs) Pop() (CleanupFunc, error) { - if len(c.fns) == 0 { - return nil, ErrEmptyStack - } - var res CleanupFunc - res, c.fns = c.fns[len(c.fns)-1], c.fns[:len(c.fns)-1] - return res, nil -} - -func CleanupCluster(ctx context.Context, log logger.Logger, fns CleanupFuncs) error { - for { - fn, err := fns.Pop() - if err != nil { - if err != ErrEmptyStack { - return fmt.Errorf("failed fetching next cleanup function: %w", err) - } - break - } - if err := fn(ctx, log); err != nil { - log.Failuref("failed cleaning up: %s", err) - } - } - - return nil -} diff --git a/cmd/gitops/beta/run/cleanup_test.go b/cmd/gitops/beta/run/cleanup_test.go deleted file mode 100644 index 4e1c2ed9ee..0000000000 --- a/cmd/gitops/beta/run/cleanup_test.go +++ /dev/null @@ -1,87 +0,0 @@ -package run_test - -import ( - "context" - "fmt" - "reflect" - "strings" - "testing" - - . "github.com/onsi/gomega" - "github.com/weaveworks/weave-gitops/cmd/gitops/beta/run" - "github.com/weaveworks/weave-gitops/pkg/logger" -) - -func TestCleanupWorksWithNil(t *testing.T) { - g := NewWithT(t) - - var s run.CleanupFuncs - f1 := func(ctx context.Context, log logger.Logger) error { return nil } - f2 := func(ctx context.Context, log logger.Logger) error { return nil } - - s.Push(f1) - s.Push(nil) // should be ignored - s.Push(f2) - - fn, err := s.Pop() - g.Expect(err).NotTo(HaveOccurred(), "pop returned an unexpected error") - g.Expect( - reflect.ValueOf(fn).Pointer()). - To(Equal(reflect.ValueOf(f2).Pointer()), "value returned from stack is not f2") - - fn, err = s.Pop() - g.Expect(err).NotTo(HaveOccurred(), "pop returned an unexpected error") - g.Expect( - reflect.ValueOf(fn).Pointer()). - To(Equal(reflect.ValueOf(f1).Pointer()), "value returned from stack is not f1") - - fn, err = s.Pop() - g.Expect(err).To(Equal(run.ErrEmptyStack), "pop returned an unexpected error") - g.Expect(fn).To(BeNil(), "unexpected value returned from stack") -} - -func TestCleanupFailsGracefullyOnConsecutiveCallsToPop(t *testing.T) { - g := NewWithT(t) - - var s run.CleanupFuncs - fn, err := s.Pop() - g.Expect(err).To(Equal(run.ErrEmptyStack), "unexpected error returned from pop") - g.Expect(fn).To(BeNil(), "unexpected value returned from pop") - - fn, err = s.Pop() - g.Expect(err).To(Equal(run.ErrEmptyStack), "unexpected error returned from pop") - g.Expect(fn).To(BeNil(), "unexpected value returned from pop") -} - -func TestCleanupClusterRunsAllFunctionsFromStackInCorrectOrder(t *testing.T) { - g := NewWithT(t) - - cnt := 0 - var s run.CleanupFuncs - - s.Push(func(ctx context.Context, log logger.Logger) error { cnt *= 2; return nil }) - s.Push(func(ctx context.Context, log logger.Logger) error { cnt += 4; return nil }) - s.Push(func(ctx context.Context, log logger.Logger) error { cnt = 3; return nil }) - - err := run.CleanupCluster(context.Background(), nil, s) - g.Expect(err).NotTo(HaveOccurred(), "unexpected error returned") - g.Expect(cnt).To(Equal(14), "unexpected execution order") -} - -func TestCleanupClusterLogsAllErrors(t *testing.T) { - g := NewWithT(t) - - cnt := 0 - var s run.CleanupFuncs - - s.Push(func(ctx context.Context, log logger.Logger) error { cnt *= 2; return nil }) - s.Push(func(ctx context.Context, log logger.Logger) error { cnt += 4; return nil }) - s.Push(func(ctx context.Context, log logger.Logger) error { return fmt.Errorf("foo") }) - s.Push(func(ctx context.Context, log logger.Logger) error { cnt = 3; return nil }) - - var buf strings.Builder - err := run.CleanupCluster(context.Background(), logger.NewCLILogger(&buf), s) - g.Expect(err).NotTo(HaveOccurred(), "function should not have returned an error") - g.Expect(cnt).To(Equal(14), "unexpected execution order") - g.Expect(buf.String()).To(Equal("✗ failed cleaning up: foo\n"), "unexpected log output") -} diff --git a/cmd/gitops/beta/run/cmd.go b/cmd/gitops/beta/run/cmd.go deleted file mode 100644 index bcd6e8ee71..0000000000 --- a/cmd/gitops/beta/run/cmd.go +++ /dev/null @@ -1,1338 +0,0 @@ -package run - -import ( - "context" - "errors" - "fmt" - "io" - "io/fs" - "os" - "os/signal" - "os/user" - "path/filepath" - "strings" - "sync" - "sync/atomic" - "syscall" - "time" - - "github.com/fluxcd/go-git-providers/gitprovider" - kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" - sourcev1 "github.com/fluxcd/source-controller/api/v1" - "github.com/fsnotify/fsnotify" - "github.com/manifoldco/promptui" - "github.com/spf13/cobra" - "github.com/weaveworks/weave-gitops/cmd/gitops/cmderrors" - "github.com/weaveworks/weave-gitops/cmd/gitops/config" - "github.com/weaveworks/weave-gitops/pkg/fluxexec" - "github.com/weaveworks/weave-gitops/pkg/fluxinstall" - "github.com/weaveworks/weave-gitops/pkg/kube" - "github.com/weaveworks/weave-gitops/pkg/logger" - "github.com/weaveworks/weave-gitops/pkg/run" - "github.com/weaveworks/weave-gitops/pkg/run/bootstrap" - "github.com/weaveworks/weave-gitops/pkg/run/constants" - "github.com/weaveworks/weave-gitops/pkg/run/install" - "github.com/weaveworks/weave-gitops/pkg/run/watch" - "github.com/weaveworks/weave-gitops/pkg/s3" - "github.com/weaveworks/weave-gitops/pkg/sourceignore" - "github.com/weaveworks/weave-gitops/pkg/validate" - "github.com/weaveworks/weave-gitops/pkg/version" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/client-go/discovery" - "k8s.io/client-go/rest" - "sigs.k8s.io/yaml" -) - -const ( - defaultDashboardName = "ww-gitops" - dashboardPodName = "ww-gitops-weave-gitops" - adminUsername = "admin" -) - -var HelmChartVersion = "3.0.0" - -type RunCommandFlags struct { - FluxVersion string - AllowK8sContext []string - Components []string - ComponentsExtra []string - Timeout time.Duration - PortForward string // port forward specifier, e.g. "port=8080:8080,resource=svc/app" - RootDir string - DecryptionKeyFile string - - // Dashboard - DashboardPort string - DashboardHashedPassword string - SkipDashboardInstall bool - DashboardImage string - DashboardValuesFiles []string - - // Session - SessionName string - SessionNamespace string - NoSession bool - SkipResourceCleanup bool - NoBootstrap bool - - // Global flags. - Namespace string - KubeConfig string - - // Flags, created by genericclioptions. - Context string - - // Hidden session name for the sub-process - HiddenSessionName string -} - -var flags RunCommandFlags - -var kubeConfigArgs *genericclioptions.ConfigFlags - -func RunCommand(opts *config.Options) *cobra.Command { - cmd := &cobra.Command{ - Use: "run", - Short: "Set up an interactive sync between your cluster and your local file system", - Long: "This will set up a sync between the cluster in your kubeconfig and the path that you specify on your local filesystem. If you do not have Flux installed on the cluster then this will add it to the cluster automatically. This is a requirement so we can sync the files successfully from your local system onto the cluster. Flux will take care of producing the objects for you.", - Example: ` -# Run the sync on the current working directory -gitops beta run . [flags] - -# Run the sync against the dev overlay path -gitops beta run ./deploy/overlays/dev - -# Run the sync on the dev directory and forward the port. -# Listen on port 8080 on localhost, forwarding to 5000 in a pod of the service app. -gitops beta run ./dev --port-forward port=8080:5000,resource=svc/app - -# Run the sync on the dev directory with a specified root dir. -gitops beta run ./clusters/default/dev --root-dir ./clusters/default - -# Run the sync on the podinfo demo. -git clone https://github.com/stefanprodan/podinfo -cd podinfo -gitops beta run ./deploy/overlays/dev --no-session --timeout 3m --port-forward namespace=dev,resource=svc/backend,port=9898:9898 - -# Run the sync on the podinfo demo in the session mode. -git clone https://github.com/stefanprodan/podinfo -cd podinfo -gitops beta run ./deploy/overlays/dev --timeout 3m --port-forward namespace=dev,resource=svc/backend,port=9898:9898 - -# Run the sync on the podinfo Helm chart, in the session mode. Please note that file Chart.yaml must exist in the directory. -git clone https://github.com/stefanprodan/podinfo -cd podinfo -gitops beta run ./charts/podinfo --timeout 3m --port-forward namespace=flux-system,resource=svc/run-dev-helm-podinfo,port=9898:9898`, - SilenceUsage: true, - SilenceErrors: true, - PreRunE: betaRunCommandPreRunE(&opts.Endpoint), - RunE: betaRunCommandRunE(opts), - DisableAutoGenTag: true, - } - - cmdFlags := cmd.Flags() - - cmdFlags.StringVar(&flags.FluxVersion, "flux-version", version.FluxVersion, "The version of Flux to install.") - cmdFlags.StringSliceVar(&flags.AllowK8sContext, "allow-k8s-context", []string{}, "The name of the KubeConfig context to explicitly allow.") - cmdFlags.StringSliceVar(&flags.Components, "components", []string{"source-controller", "kustomize-controller", "helm-controller", "notification-controller"}, "The Flux components to install.") - cmdFlags.StringSliceVar(&flags.ComponentsExtra, "components-extra", []string{}, "Additional Flux components to install, allowed values are image-reflector-controller,image-automation-controller.") - cmdFlags.DurationVar(&flags.Timeout, "timeout", 5*time.Minute, "The timeout for operations during GitOps Run.") - cmdFlags.StringVar(&flags.PortForward, "port-forward", "", "Forward the port from a cluster's resource to your local machine i.e. 'port=8080:8080,resource=svc/app'.") - cmdFlags.StringVar(&flags.DashboardPort, "dashboard-port", "9001", "GitOps Dashboard port") - cmdFlags.BoolVar(&flags.SkipDashboardInstall, "skip-dashboard-install", false, "Skip installation of the Dashboard. This also disables the prompt asking whether the Dashboard should be installed.") - cmdFlags.StringVar(&flags.DashboardHashedPassword, "dashboard-hashed-password", "", "GitOps Dashboard password in BCrypt hash format") - cmdFlags.StringVar(&flags.RootDir, "root-dir", "", "Specify the root directory to watch for changes. If not specified, the root of Git repository will be used.") - cmdFlags.StringVar(&flags.SessionName, "session-name", getSessionNameFromGit(), "Specify the name of the session. If not specified, the name of the current branch and the last commit id will be used.") - cmdFlags.StringVar(&flags.SessionNamespace, "session-namespace", "default", "Specify the namespace of the session.") - cmdFlags.BoolVar(&flags.NoSession, "no-session", false, "Disable session management. If not specified, the session will be enabled by default.") - cmdFlags.BoolVar(&flags.NoBootstrap, "no-bootstrap", false, "Disable bootstrapping at shutdown.") - cmdFlags.BoolVar(&flags.SkipResourceCleanup, "skip-resource-cleanup", false, "Skip resource cleanup. If not specified, the GitOps Run resources will be deleted by default.") - cmdFlags.StringVar(&flags.DecryptionKeyFile, "decryption-key-file", "", "Path to an age key file used for decrypting Secrets using SOPS.") - - cmdFlags.StringSliceVar(&flags.DashboardValuesFiles, "values", nil, "Local path to values.yaml files for HelmRelease, also accepts comma-separated values.") - cmdFlags.StringVar(&flags.DashboardImage, "dashboard-image", "", "Override GitOps Dashboard image") - _ = cmdFlags.MarkHidden("dashboard-image") - - cmdFlags.StringVar(&flags.HiddenSessionName, "x-session-name", "", "The session name acknowledged by the sub-process. This is a hidden flag and should not be used.") - _ = cmdFlags.MarkHidden("x-session-name") - - kubeConfigArgs = run.GetKubeConfigArgs() - - kubeConfigArgs.AddFlags(cmd.Flags()) - - return cmd -} - -func getSessionNameFromGit() string { - const prefix = "run" - - branch, err := run.GetBranchName() - if err != nil { - return "" - } - - commit, err := run.GetCommitID() - if err != nil { - return "" - } - - isDirty, err := run.IsDirty() - if err != nil { - return "" - } - - sessionName := fmt.Sprintf("%s-%s-%s", prefix, branch, commit) - if isDirty { - sessionName = fmt.Sprintf("%s-%s-%s-dirty", prefix, branch, commit) - } - - sessionName = strings.ToLower(strings.ReplaceAll(sessionName, "/", "-")) - - return sessionName -} - -func betaRunCommandPreRunE(endpoint *string) func(*cobra.Command, []string) error { - return func(cmd *cobra.Command, args []string) error { - numArgs := len(args) - - if numArgs == 0 { - return cmderrors.ErrNoFilePath - } - - if numArgs > 1 { - return cmderrors.ErrMultipleFilePaths - } - - return nil - } -} - -func getKubeClient(cmd *cobra.Command) (*kube.KubeHTTP, *rest.Config, error) { - var err error - - log := logger.NewCLILogger(os.Stdout) - - if flags.Namespace, err = cmd.Flags().GetString("namespace"); err != nil { - return nil, nil, err - } - - kubeConfigArgs.Namespace = &flags.Namespace - - if flags.KubeConfig, err = cmd.Flags().GetString("kubeconfig"); err != nil { - return nil, nil, err - } - - if flags.Context, err = cmd.Flags().GetString("context"); err != nil { - return nil, nil, err - } - - if flags.KubeConfig != "" { - kubeConfigArgs.KubeConfig = &flags.KubeConfig - - if flags.Context == "" { - log.Failuref("A context should be provided if a kubeconfig is provided") - return nil, nil, cmderrors.ErrNoContextForKubeConfig - } - } - - log.Actionf("Checking for a cluster in the kube config ...") - - var contextName string - - if flags.Context != "" { - contextName = flags.Context - } else { - _, contextName, err = kube.RestConfig() - if err != nil { - log.Failuref("Error getting a restconfig: %v", err.Error()) - return nil, nil, cmderrors.ErrNoCluster - } - } - - cfg, err := kubeConfigArgs.ToRESTConfig() - if err != nil { - return nil, nil, fmt.Errorf("error getting a restconfig from kube config args: %w", err) - } - - kubeClientOpts := run.GetKubeClientOptions() - kubeClientOpts.BindFlags(cmd.Flags()) - - kubeClient, err := run.GetKubeClient(log, contextName, cfg, kubeClientOpts) - if err != nil { - return nil, nil, cmderrors.ErrGetKubeClient - } - - return kubeClient, cfg, nil -} - -func fluxStep(log logger.Logger, kubeClient *kube.KubeHTTP) (fluxVersion *install.FluxVersionInfo, justInstalled bool, err error) { - ctx := context.Background() - - log.Actionf("Checking if Flux is already installed ...") - - guessed := false - if fluxVersion, guessed, err = install.GetFluxVersion(ctx, log, kubeClient); err != nil { - log.Warningf("Flux is not found: %v", err.Error()) - - product := fluxinstall.NewProduct(flags.FluxVersion) - - installer := fluxinstall.NewInstaller() - - execPath, err := installer.Ensure(ctx, product) - if err != nil { - execPath, err = installer.Install(ctx, product) - if err != nil { - return nil, false, err - } - } - - wd, err := os.Getwd() - if err != nil { - return nil, false, err - } - - flux, err := fluxexec.NewFlux(wd, execPath) - if err != nil { - return nil, false, err - } - - // This means that Flux logs will be printed to the console, but not be sent to S3 - flux.SetLogger(log.L()) - - var components []fluxexec.Component - for _, component := range flags.Components { - components = append(components, fluxexec.Component(component)) - } - - var componentsExtra []fluxexec.ComponentExtra - for _, component := range flags.ComponentsExtra { - componentsExtra = append(componentsExtra, fluxexec.ComponentExtra(component)) - } - - if err := flux.Install(ctx, - fluxexec.Components(components...), - fluxexec.ComponentsExtra(componentsExtra...), - fluxexec.WithGlobalOptions( - fluxexec.Namespace(flags.Namespace), - fluxexec.Timeout(flags.Timeout), - ), - ); err != nil { - return nil, false, err - } - - return &install.FluxVersionInfo{ - FluxVersion: flags.FluxVersion, - FluxNamespace: flags.Namespace, - }, true, nil - } else { - if guessed { - log.Warningf("Flux version could not be determined, assuming %s and namespace %s by mapping from the version of the Source controller %s", fluxVersion.FluxVersion, fluxVersion.FluxNamespace, fluxVersion.SourceControllerVersion) - } else { - log.Successf("Flux %s is already installed on the %s namespace.", fluxVersion.FluxVersion, fluxVersion.FluxNamespace) - } - } - - return fluxVersion, false, nil -} - -// fluentBitStep installs Fluent Bit on the cluster and returns a CleanupFunc to remove it again. The function -// ascertains that Fluent Bit is ready before returning. -func fluentBitStep(ctx context.Context, log logger.Logger, kubeClient *kube.KubeHTTP, devBucketHTTPPort int32) (CleanupFunc, error) { - err := install.InstallFluentBit(ctx, log, kubeClient, flags.Namespace, constants.GitOpsRunNamespace, install.FluentBitHRName, logger.PodLogBucketName, devBucketHTTPPort) - - if err != nil { - log.Failuref("Fluent Bit installation failed: %v", err.Error()) - return nil, err - } - - return func(ctx context.Context, log0 logger.Logger) error { - return install.UninstallFluentBit(ctx, log0, kubeClient, flags.Namespace, install.FluentBitHRName) - }, nil -} - -func dashboardStep(ctx context.Context, log logger.Logger, kubeClient *kube.KubeHTTP, generateManifestsOnly bool, dashboardHashedPassword string, dashboardValuesFiles []string) (install.DashboardType, []byte, string, error) { - log.Actionf("Checking if GitOps Dashboard is already installed ...") - - dashboardType, dashboardName, err := install.GetInstalledDashboard(ctx, kubeClient, flags.Namespace, map[install.DashboardType]bool{ - install.DashboardTypeOSS: true, install.DashboardTypeEnterprise: true, - }) - if err != nil { - return dashboardType, nil, "", fmt.Errorf("error getting installed dashboard: %w", err) - } - - shouldReconcileDashboard := false - var dashboardManifests []byte - - switch dashboardType { - case install.DashboardTypeEnterprise: - flags.SkipDashboardInstall = true - log.Warningf("GitOps Enterprise Dashboard was found. GitOps OSS Dashboard will not be installed") - return dashboardType, nil, "", err - case install.DashboardTypeOSS: - log.Warningf("GitOps Dashboard was found") - return dashboardType, nil, "", err - default: - wantToInstallTheDashboard := false - if dashboardHashedPassword != "" { - wantToInstallTheDashboard = true - } else if !flags.SkipDashboardInstall && dashboardHashedPassword == "" { - prompt := promptui.Prompt{ - Label: "Would you like to install the GitOps Dashboard", - IsConfirm: true, - Default: "Y", - } - - // Answering "n" causes err to not be nil. Hitting enter without typing - // does not return the default. - answer, err := prompt.Run() - if err == nil { - wantToInstallTheDashboard = true - } else if answer == "n" || answer == "N" { - wantToInstallTheDashboard = false - flags.SkipDashboardInstall = true - } - } - - if !wantToInstallTheDashboard { - break - } - - passwordHash := "" - if dashboardHashedPassword == "" { - password, err := install.ReadPassword(log) - if err != nil { - return install.DashboardTypeNone, nil, "", err - } - - if password == "" { - return install.DashboardTypeNone, nil, "", fmt.Errorf("dashboard password is an empty string") - } - - passwordHash, err = install.GeneratePasswordHash(log, password) - if err != nil { - return install.DashboardTypeNone, nil, "", err - } - } else { - passwordHash = dashboardHashedPassword - } - - dashboardObjects, err := install.CreateDashboardObjects(log, defaultDashboardName, flags.Namespace, adminUsername, passwordHash, HelmChartVersion, flags.DashboardImage, dashboardValuesFiles) - if err != nil { - return install.DashboardTypeNone, nil, "", fmt.Errorf("error creating dashboard objects: %w", err) - } - - if generateManifestsOnly { - return install.DashboardTypeNone, dashboardObjects.Manifests, passwordHash, nil - } - - if err := install.InstallDashboard(ctx, log, kubeClient, dashboardObjects); err != nil { - return install.DashboardTypeNone, nil, "", fmt.Errorf("gitops dashboard installation failed: %w", err) - } else { - dashboardType = install.DashboardTypeOSS - dashboardName = defaultDashboardName - shouldReconcileDashboard = true - - log.Successf("GitOps Dashboard has been installed") - } - } - - if dashboardType == install.DashboardTypeOSS && shouldReconcileDashboard { - log.Actionf("Request reconciliation of dashboard (timeout %v) ...", flags.Timeout) - - if dashboardName == "" { - dashboardName = defaultDashboardName - } - - if err := install.ReconcileDashboard(ctx, kubeClient, dashboardName, flags.Namespace, dashboardPodName, flags.Timeout); err != nil { - log.Failuref("Error requesting reconciliation of dashboard: %v", err.Error()) - return install.DashboardTypeNone, nil, "", err - } else { - log.Successf("Dashboard reconciliation is done.") - } - } - - return dashboardType, dashboardManifests, "", nil -} - -func runCommandOuterProcess(cmd *cobra.Command, args []string) (retErr error) { - paths, err := run.NewPaths(args[0], flags.RootDir) - if err != nil { - return err - } - - kubeClient, _, err := getKubeClient(cmd) - if err != nil { - return err - } - - // create session - sessionLog := logger.NewCLILogger(os.Stdout) - sessionLog.Actionf("Preparing the cluster for GitOps Run session ...\n") - - sessionLog.Println("You can run `gitops beta run --no-session` to disable session management.\n") - - sessionLog.Println("If you are running GitOps Run for the first time, it may take a few minutes to download the required images.") - sessionLog.Println("GitOps Run session is also required to install Flux components, if it is not installed yet.") - sessionLog.Println("You may see Flux installation logs in the next step.\n") - - // showing Flux installation twice is confusing - log := logger.NewCLILogger(io.Discard) - - var ( - fluxJustInstalled bool - fluxVersionInfo *install.FluxVersionInfo - ) - - if fluxVersionInfo, fluxJustInstalled, err = fluxStep(log, kubeClient); err != nil { - return fmt.Errorf("failed to detect or install Flux on the host cluster: %v", err) - } - - dashboardType, dashboardManifests, dashboardHashedPassword, err := dashboardStep(context.Background(), log, kubeClient, true, flags.DashboardHashedPassword, flags.DashboardValuesFiles) - if err != nil && !errors.Is(err, install.ErrDashboardInstalled) { - return fmt.Errorf("failed to generate dashboard manifests: %v", err) - } - - sessionLog.Actionf("Creating GitOps Run session %s in namespace %s ...", flags.SessionName, flags.SessionNamespace) - - sessionLog.Println("\nYou may see Flux installation logs again, as it is being installed inside the session.\n") - - portForwardsForSession := []string{} - if dashboardType == install.DashboardTypeOSS && err == nil { - portForwardsForSession = append(portForwardsForSession, flags.DashboardPort) - } - - if flags.PortForward != "" { - spec, err := watch.ParsePortForwardSpec(flags.PortForward) - if err != nil { - return err - } - - portForwardsForSession = append(portForwardsForSession, spec.HostPort) - } - - var kind string - if yes, err := isHelm(paths.GetAbsoluteTargetDir()); yes && err == nil { - kind = "helm" - } else if !yes && err == nil { - kind = "ks" - } else { - return err - } - - session, err := install.NewSession( - sessionLog, - kubeClient, - flags.SessionName, - flags.SessionNamespace, - fluxVersionInfo.FluxNamespace, // flux namespace of the session - portForwardsForSession, - flags.SkipDashboardInstall, - dashboardHashedPassword, - kind, - ) - - if err != nil { - return err - } - - sessionLog.Actionf("Waiting for GitOps Run session %s to be ready ...", flags.SessionName) - - if err := session.Start(); err != nil { - return err - } - - sessionLog.Successf("Session %s is ready", flags.SessionName) - - sessionLog.Actionf("Connecting to GitOps Run session %s ...", flags.SessionName) - - if err := session.Connect(); err != nil { - return err - } - - sessionLog.Println("") - sessionLog.Actionf("Deleting GitOps Run session %s ...", flags.SessionName) - - if err := session.Close(); err != nil { - sessionLog.Failuref("Failed to delete session %s: %v", flags.SessionName, err) - return err - } else { - sessionLog.Successf("Session %s is deleted successfully", flags.SessionName) - } - - // now that the session is deleted, we return to the host cluster - - // run bootstrap wizard only if Flux was not installed - if fluxJustInstalled && !flags.NoBootstrap { - prompt := promptui.Prompt{ - Label: "Would you like to bootstrap your cluster into GitOps mode", - IsConfirm: true, - Default: "Y", - } - - _, err = prompt.Run() - if err != nil { - return nil - } - - for { - err := runBootstrap(context.Background(), log, paths, dashboardManifests) - if err == nil { - break - } - - log.Warningf("Error bootstrapping: %v", err) - - prompt := promptui.Prompt{ - Label: "Couldn't bootstrap - would you like to try again", - IsConfirm: true, - Default: "Y", - } - - _, err = prompt.Run() - if err != nil { - return nil - } - } - } - - return err -} - -func runCommandInnerProcess(cmd *cobra.Command, args []string) error { - // There are two loggers in this function. - // 1. log0 is the os.Stdout logger - // 2. log is the S3 logger that also delegates its outputs to "log0". - log0 := logger.NewCLILogger(os.Stdout) - - paths, err := run.NewPaths(args[0], flags.RootDir) - if err != nil { - return err - } - - kubeClient, cfg, err := getKubeClient(cmd) - if err != nil { - return err - } - - discoveryClient, err := discovery.NewDiscoveryClientForConfig(cfg) - if err != nil { - return err - } - - serverVersion, err := discoveryClient.ServerVersion() - if err != nil { - return err - } - - // We need the server version to pass to the validation package - kubernetesVersion := trimK8sVersion(serverVersion.GitVersion) - - contextName := kubeClient.ClusterName - validAllowedContext := false - - for _, allowContext := range flags.AllowK8sContext { - if allowContext == contextName { - log0.Actionf("Explicitly allow GitOps Run on %s context", contextName) - - validAllowedContext = true - - break - } - } - - if !validAllowedContext { - if !run.IsLocalCluster(kubeClient) { - return fmt.Errorf("to run against a remote cluster, use --allow-k8s-context=%s", contextName) - } - } - - ctx, cancel := context.WithCancel(context.Background()) - sigs := make(chan os.Signal, 1) - // Subscribe to SIGUSR1 in addition to the usual signals because in session mode - // the outer process traps SIGTERM and SIGINT and sends SIGUSR1 to the inner process. - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR1) - - cleanupFns := CleanupFuncs{} - cleanupFns.Push(func(ctx context.Context, log logger.Logger) error { - cancel() - return nil - }) - - // Using defer instead - // if CleanupCluster already called during the process, the stack will be empty and nothing will happen. - defer func(ctx context.Context, log logger.Logger, fns CleanupFuncs) { - err := CleanupCluster(ctx, log, fns) - if err != nil { - log.Failuref("Error cleaning up: %v", err) - } - }(ctx, log0 /*logger.NewCLILogger(io.Discard)*/, cleanupFns) - - go func() { - select { - case <-ctx.Done(): - case sig := <-sigs: - log0.Actionf("Received %s, quitting...", sig) - // re-enable listening for ctrl+C - signal.Reset(sig) - cancel() - return - } - }() - - var ( - fluxJustInstalled bool - fluxVersionInfo *install.FluxVersionInfo - ) - - fluxVersionInfo, fluxJustInstalled, err = fluxStep(log0, kubeClient) - if err != nil { - return err - } - - sessionName := flags.HiddenSessionName - if sessionName == "" { - sessionName = "no-session" - } - - var username string - if current, err := user.Current(); err != nil { - username = "unknown" - } else { - username = current.Username - } - - // ====================== Dev-bucket ====================== - // Install dev-bucket server before everything, so that we can also forward logs to it - unusedPorts, err := run.GetUnusedPorts(2) - if err != nil { - return err - } - - devBucketHTTPPort := unusedPorts[0] - devBucketHTTPSPort := unusedPorts[1] - - // generate access key and secret key for Minio auth - accessKey, err := s3.GenerateAccessKey(s3.DefaultRandIntFunc) - if err != nil { - return fmt.Errorf("failed generating access key: %w", err) - } - - secretKey, err := s3.GenerateSecretKey(s3.DefaultRandIntFunc) - if err != nil { - return fmt.Errorf("failed generating secret key: %w", err) - } - - cancelDevBucketPortForwarding, cert, err := watch.InstallDevBucketServer(ctx, log0, kubeClient, cfg, devBucketHTTPPort, devBucketHTTPSPort, accessKey, secretKey) - cleanupFns.Push(func(ctx context.Context, log logger.Logger) error { - cancelDevBucketPortForwarding() - return watch.UninstallDevBucketServer(ctx, log, kubeClient) - }) - if err != nil { - return fmt.Errorf("unable to install S3 bucket server: %w", err) - } - - minioClient, err := s3.NewMinioClient(fmt.Sprintf("localhost:%d", devBucketHTTPSPort), accessKey, secretKey, cert) - if err != nil { - return err - } - - if err := logger.CreateBucket(minioClient, logger.SessionLogBucketName); err != nil { - return err - } - - if err := logger.CreateBucket(minioClient, logger.PodLogBucketName); err != nil { - return err - } - - log, err := logger.NewS3LogWriter(minioClient, sessionName, log0) - if err != nil { - return fmt.Errorf("failed creating S3 log writer: %w", err) - } - - // ====================== Fluent-Bit ===================== - fbCleanupFn, err := fluentBitStep(ctx, log, kubeClient, devBucketHTTPPort) - cleanupFns.Push(fbCleanupFn) - if err != nil { - return err - } - - // ====================== Dashboard ====================== - var ( - dashboardType install.DashboardType - dashboardErr error - dashboardManifests []byte - ) - - dashboardType, dashboardManifests, _, dashboardErr = dashboardStep(ctx, log, kubeClient, false, flags.DashboardHashedPassword, flags.DashboardValuesFiles) - if err != nil && !errors.Is(err, install.ErrDashboardInstalled) { - cancel() - return err - } - - var cancelDashboardPortForwarding func() = nil - - if dashboardType == install.DashboardTypeOSS && dashboardErr == nil { - cancelDashboardPortForwarding, err = watch.EnablePortForwardingForDashboard(ctx, log, kubeClient, cfg, flags.Namespace, dashboardPodName, flags.DashboardPort) - cleanupFns.Push(func(ctx context.Context, log logger.Logger) error { - cancelDashboardPortForwarding() - return nil - }) - if err != nil { - return err - } - } - - if err := watch.InitializeRootDir(log, paths.RootDir); err != nil && !errors.Is(err, sourceignore.ErrIgnoreFileExists) { - return fmt.Errorf("couldn't initialize root dir %s: %w", paths.RootDir, err) - } - - if err := watch.InitializeTargetDir(paths.GetAbsoluteTargetDir()); err != nil { - return fmt.Errorf("couldn't set up against target %s: %w", paths.TargetDir, err) - } - - setupParams := watch.SetupRunObjectParams{ - Namespace: flags.Namespace, - Path: paths.TargetDir, - Timeout: flags.Timeout, - DevBucketPort: devBucketHTTPPort, - SessionName: sessionName, - Username: username, - AccessKey: accessKey, - SecretKey: secretKey, - DecryptionKeyFile: flags.DecryptionKeyFile, - } - - if yes, err := isHelm(paths.GetAbsoluteTargetDir()); yes && err == nil { - err := watch.SetupBucketSourceAndHelm(ctx, log, kubeClient, setupParams) - cleanupFns.Push(func(ctx context.Context, log logger.Logger) error { - if !flags.SkipResourceCleanup { - return watch.CleanupBucketSourceAndHelm(ctx, log, kubeClient, flags.Namespace) - } - return nil - }) - if err != nil { - return err - } - } else if !yes && err == nil { - err := watch.SetupBucketSourceAndKS(ctx, log, kubeClient, setupParams) - cleanupFns.Push(func(ctx context.Context, log logger.Logger) error { - if !flags.SkipResourceCleanup { - return watch.CleanupBucketSourceAndKS(ctx, log, kubeClient, flags.Namespace) - } - return nil - }) - if err != nil { - return err - } - } else if err != nil { - log.Actionf("Unable to determine if target is a Helm or Kustomization directory: %v", err) - return err - } - - minioClient, err = s3.NewMinioClient(fmt.Sprintf("localhost:%d", devBucketHTTPSPort), accessKey, secretKey, cert) - if err != nil { - return err - } - - // watch for file changes in dir gitRepoRoot - watcher, err := fsnotify.NewWatcher() - if err != nil { - return err - } - - ignorer := watch.CreateIgnorer(paths.RootDir) - - err = filepath.Walk(paths.RootDir, watch.WatchDirsForFileWalker(watcher, ignorer)) - if err != nil { - return err - } - - // cancel function to stop forwarding port - var ( - cancelPortFwd func() - // atomic counter for the number of file change events that have changed - counter uint64 = 1 - needToRescan = false - ) - - watcherCtx, watcherCancel := context.WithCancel(ctx) - lastReconcile := time.Now() - closeOnce := sync.Once{} - stopUploadCh := make(chan struct{}) - cleanupFns.Push(func(ctx context.Context, log logger.Logger) error { - watcherCancel() - closeOnce.Do(func() { - close(stopUploadCh) - }) - return nil - }) - - go func() { - for { - select { - case <-watcherCtx.Done(): - return - case <-stopUploadCh: - return - case event := <-watcher.Events: - info, err := os.Stat(event.Name) - if err != nil { - continue - } - if event.Op&fsnotify.Create == fsnotify.Create || - event.Op&fsnotify.Remove == fsnotify.Remove || - event.Op&fsnotify.Rename == fsnotify.Rename { - // if it's a dir, we need to watch it - if info.IsDir() { - needToRescan = true - } - } - - // Skip all dotfiles because these are usually created by editors as swap files. A reconciliation - // should only be triggered by files that are actually part of the application being run. - base := filepath.Base(event.Name) - - if !info.IsDir() && strings.HasPrefix(base, ".") && base != sourceignore.IgnoreFilename { - continue - } - - if cancelPortFwd != nil { - cancelPortFwd() - } - - // If there are still changes and it's been a few seconds, - // cancel the old context and start over. - if time.Since(lastReconcile) > (10 * time.Second) { - watcherCancel() - watcherCtx, watcherCancel = context.WithCancel(ctx) - } - - atomic.AddUint64(&counter, 1) - case err := <-watcher.Errors: - if err != nil { - log.Failuref("Error: %v", err) - } - } - } - }() - - // event aggregation loop - ticker := time.NewTicker(680 * time.Millisecond) - - go func() { - for { //nolint:gosimple - select { - case <-stopUploadCh: - return - case <-ticker.C: - if counter > 0 { - log.Actionf("%d change events detected", counter) - - // reset counter - atomic.StoreUint64(&counter, 0) - - // we have to skip validation for helm charts - if yes, err := isHelm(paths.GetAbsoluteTargetDir()); !yes && err == nil { - // validate only files under the target dir - log.Actionf("Validating files under %s/ ...", paths.TargetDir) - - if err := validate.Validate(log, paths.GetAbsoluteTargetDir(), paths.RootDir, kubernetesVersion, fluxVersionInfo.FluxVersion); err != nil { - log.Failuref("Validation failed: please review the errors and try again: %v", err) - continue - } - } - - // use ctx, not thisCtx - incomplete uploads will never make anybody happy - if err := watch.SyncDir(ctx, log, paths.RootDir, constants.RunDevBucketName, minioClient, ignorer); err != nil { - log.Failuref("Error syncing dir: %v", err) - } - - if needToRescan { - // close the old watcher - if err := watcher.Close(); err != nil { - log.Warningf("Error closing the old watcher: %v", err) - } - // create a new watcher - watcher, err = fsnotify.NewWatcher() - if err != nil { - log.Failuref("Error creating new watcher: %v", err) - } - - err = filepath.Walk(paths.RootDir, watch.WatchDirsForFileWalker(watcher, ignorer)) - if err != nil { - log.Failuref("Error re-walking dir: %v", err) - } - - needToRescan = false - } - - log.Actionf("Request reconciliation of GitOps Run resources (timeout %v) ... ", flags.Timeout) - - lastReconcile = time.Now() - // context that cancels when files change - thisCtx := watcherCtx - - var reconcileErr error - if yes, err := isHelm(paths.GetAbsoluteTargetDir()); yes && err == nil { - reconcileErr = watch.ReconcileDevBucketSourceAndHelm(thisCtx, log, kubeClient, flags.Namespace, flags.Timeout) - } else if !yes && err == nil { - reconcileErr = watch.ReconcileDevBucketSourceAndKS(thisCtx, log, kubeClient, flags.Namespace, flags.Timeout) - } else if err != nil { - log.Actionf("Unable to determine if target is a Helm or Kustomization directory: %v", err) - reconcileErr = err - } - - if reconcileErr != nil { - if errors.Is(reconcileErr, context.Canceled) { - log.Actionf("Context canceled, skipping reconciliation.") - } else { - log.Failuref("Error requesting reconciliation: %v", reconcileErr) - } - } else { - log.Successf("Reconciliation is done.") - } - - portForwards := map[rune]watch.PortForwardShortcut{} - - if dashboardType == install.DashboardTypeOSS && dashboardErr == nil { - portForwardKey, err := watch.GetNextPortForwardKey(portForwards) - if err != nil { - log.Failuref("Error adding a portForward: %v", err) - } else { - portForwards[portForwardKey] = watch.PortForwardShortcut{ - Name: defaultDashboardName, - HostPort: flags.DashboardPort, - } - } - } - - var specMap *watch.PortForwardSpec - - if flags.PortForward != "" { - specMap, err = watch.ParsePortForwardSpec(flags.PortForward) - if err != nil { - log.Failuref("Error parsing port forward spec: %v", err) - } else { - serviceName := specMap.Name - if serviceName == "" { - serviceName = "service" - } - - portForwardKey, err := watch.GetNextPortForwardKey(portForwards) - if err != nil { - log.Failuref("Error adding a port forward: %v", err) - } else { - portForwards[portForwardKey] = watch.PortForwardShortcut{ - Name: serviceName, - HostPort: specMap.HostPort, - } - } - } - } - - if len(portForwards) > 0 { - watch.ShowPortForwards(ctx, log, portForwards) - } - - if specMap != nil { - // get pod from specMap - namespacedName := types.NamespacedName{Namespace: specMap.Namespace, Name: specMap.Name} - - var ( - pod *corev1.Pod - podErr error - ) - - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - if pollErr := wait.PollImmediate(2*time.Second, flags.Timeout, func() (bool, error) { - pod, podErr = run.GetPodFromResourceDescription(thisCtx, kubeClient, namespacedName, specMap.Kind, nil) - if pod != nil && podErr == nil { - return true, nil - } - - log.Waitingf("Waiting for a pod from specMap: %v", podErr) - return false, nil - }); pollErr != nil { - log.Failuref("Waiting for a pod from specMap: %v", pollErr) - } - - if pod == nil { - log.Failuref("Error getting pod from specMap") - } else /* pod is available */ { - waitFwd := make(chan struct{}, 1) - readyChannel := make(chan struct{}) - cancelPortFwd = func() { - close(waitFwd) - - cancelPortFwd = nil - } - - log.Actionf("Port forwarding to pod %s/%s ...", pod.Namespace, pod.Name) - - // this function _BLOCKS_ until the stopChannel (waitPwd) is closed. - if err := watch.ForwardPort(log.L(), pod, cfg, specMap, waitFwd, readyChannel); err != nil { - log.Failuref("Error forwarding port: %v", err) - } - - log.Successf("Port forwarding is stopped.") - } - } - } - } - } - }() - - // wait for interrupt or ctrl+C - log.Waitingf("Press Ctrl+C to stop GitOps Run ...") - - <-ctx.Done() - - closeOnce.Do(func() { - close(stopUploadCh) - }) - - if err := watcher.Close(); err != nil { - log.Warningf("Error closing watcher: %v", err.Error()) - } - - // print a blank line to make it easier to read the logs - fmt.Println() - - if cancelDashboardPortForwarding != nil { - cancelDashboardPortForwarding() - } - - ticker.Stop() - - // this is the default behaviour - if !flags.SkipResourceCleanup { - // create new context that isn't cancelled, for cleanup and bootstrapping - ctx = context.Background() - if err := CleanupCluster(ctx, log0, cleanupFns); err != nil { - return fmt.Errorf("failed cleaning up: %w", err) - } - } - - // run bootstrap wizard only if Flux was not installed - if fluxJustInstalled && !flags.NoBootstrap { - prompt := promptui.Prompt{ - Label: "Would you like to bootstrap your cluster into GitOps mode", - IsConfirm: true, - Default: "Y", - } - - _, err = prompt.Run() - if err != nil { - return nil - } - - for { - err := runBootstrap(ctx, log0, paths, dashboardManifests) - if err == nil { - break - } - - log0.Warningf("Error bootstrapping: %v", err) - - prompt := promptui.Prompt{ - Label: "Couldn't bootstrap - would you like to try again", - IsConfirm: true, - Default: "Y", - } - - _, err = prompt.Run() - if err != nil { - return nil - } - } - } - - return nil -} - -func isHelm(dir string) (bool, error) { - _, err := os.Stat(filepath.Join(dir, "Chart.yaml")) - if err != nil && os.IsNotExist(err) { - // check Chart.yml - _, err = os.Stat(filepath.Join(dir, "Chart.yml")) - if err != nil && os.IsNotExist(err) { - return false, nil - } else if err != nil { - return false, err - } - } else if err != nil { - return false, err - } - - return true, nil -} - -func runBootstrap(ctx context.Context, log logger.Logger, paths *run.Paths, manifests []byte) (err error) { - // parse remote - repo, err := bootstrap.ParseGitRemote(log, paths.RootDir) - if err != nil { - log.Failuref("Error parsing Git remote: %v", err.Error()) - } - - // run the bootstrap wizard - log.Actionf("Starting bootstrap wizard ...") - - host := bootstrap.GetHost(repo) - gitProvider := bootstrap.ParseGitProvider(host) - - log.Waitingf("Press Ctrl+C to stop bootstrap wizard ...") - - if gitProvider == bootstrap.GitProviderUnknown { - gitProvider, err = bootstrap.SelectGitProvider(log) - if err != nil { - log.Failuref("Error selecting git provider: %v", err.Error()) - } - } - - wizard, err := bootstrap.NewBootstrapWizard(log, gitProvider, repo) - - if err != nil { - return fmt.Errorf("error creating bootstrap wizard: %v", err.Error()) - } - - if err = wizard.Run(log); err != nil { - return fmt.Errorf("error running bootstrap wizard: %v", err.Error()) - } - - params := wizard.BuildCmd(log) - - product := fluxinstall.NewProduct(flags.FluxVersion) - - installer := fluxinstall.NewInstaller() - - execPath, err := installer.Ensure(ctx, product) - if err != nil { - execPath, err = installer.Install(ctx, product) - if err != nil { - return err - } - } - - wd, err := os.Getwd() - if err != nil { - return err - } - - flux, err := fluxexec.NewFlux(wd, execPath) - if err != nil { - return err - } - - flux.SetLogger(log.L()) - - slugifiedWorkloadPath := strings.ReplaceAll(paths.TargetDir, "/", "-") - - workloadKustomizationPath := strings.Join([]string{paths.ClusterDir, slugifiedWorkloadPath, slugifiedWorkloadPath + "-kustomization.yaml"}, "/") - workloadKustomization := kustomizev1.Kustomization{ - TypeMeta: metav1.TypeMeta{ - Kind: kustomizev1.KustomizationKind, - APIVersion: kustomizev1.GroupVersion.Identifier(), - }, - - ObjectMeta: metav1.ObjectMeta{ - Name: slugifiedWorkloadPath, - Namespace: flags.Namespace, - }, - Spec: kustomizev1.KustomizationSpec{ - Interval: metav1.Duration{Duration: 1 * time.Hour}, - Prune: true, // GC the kustomization - SourceRef: kustomizev1.CrossNamespaceSourceReference{ - Kind: sourcev1.GitRepositoryKind, - Name: "flux-system", - Namespace: "flux-system", - }, - Timeout: &metav1.Duration{Duration: 5 * time.Minute}, - Path: "./" + paths.TargetDir, - Wait: true, - }, - } - - workloadKustomizationContent, err := yaml.Marshal(workloadKustomization) - if err != nil { - return err - } - - workloadKustomizationContent, err = install.SanitizeResourceData(log, workloadKustomizationContent) - if err != nil { - return err - } - - workloadKustomizationContentStr := string(workloadKustomizationContent) - - commitFiles := []gitprovider.CommitFile{{ - Path: &workloadKustomizationPath, - Content: &workloadKustomizationContentStr, - }} - - if len(manifests) > 0 { - strManifests := string(manifests) - dashboardPath := strings.Join([]string{paths.ClusterDir, "weave-gitops", "dashboard.yaml"}, "/") - - commitFiles = append(commitFiles, gitprovider.CommitFile{ - Path: &dashboardPath, - Content: &strManifests, - }) - } - - err = filepath.WalkDir(paths.GetAbsoluteTargetDir(), func(path string, entry fs.DirEntry, err error) error { - if err != nil { - log.Warningf("Error: %v", err.Error()) - return err - } - if entry.IsDir() { - return nil - } - content, err := os.ReadFile(path) - strContent := string(content) - if err != nil { - log.Warningf("Error: %v", err.Error()) - return err - } - relativePath, err := run.GetRelativePathToRootDir(paths.RootDir, path) - if err != nil { - log.Warningf("Error: %v", err.Error()) - return err - } - commitFiles = append(commitFiles, gitprovider.CommitFile{ - Path: &relativePath, - Content: &strContent, - }) - return nil - }) - if err != nil { - return err - } - - bs := bootstrap.NewBootstrap(paths.ClusterDir, params.Options, params.Provider) - - err = bs.RunBootstrapCmd(ctx, flux) - if err != nil { - return err - } - - err = bs.SyncResources(ctx, log, commitFiles) - if err != nil { - return err - } - - return nil -} - -func betaRunCommandRunE(opts *config.Options) func(*cobra.Command, []string) error { - return func(cmd *cobra.Command, args []string) error { - if flags.NoSession { - return runCommandInnerProcess(cmd, args) - } else { - return runCommandOuterProcess(cmd, args) - } - } -} diff --git a/cmd/gitops/beta/run/trim_k8s_version.go b/cmd/gitops/beta/run/trim_k8s_version.go deleted file mode 100644 index c302159b92..0000000000 --- a/cmd/gitops/beta/run/trim_k8s_version.go +++ /dev/null @@ -1,14 +0,0 @@ -package run - -import ( - "regexp" - "strings" -) - -func trimK8sVersion(version string) string { - // match v1.18.0 with regex - regex := regexp.MustCompile(`^v?\d+\.\d+\.\d+`) - firstPart := regex.FindString(version) - - return strings.TrimPrefix(firstPart, "v") -} diff --git a/cmd/gitops/beta/run/trim_k9s_version_test.go b/cmd/gitops/beta/run/trim_k9s_version_test.go deleted file mode 100644 index bcb42b0933..0000000000 --- a/cmd/gitops/beta/run/trim_k9s_version_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package run - -import "testing" - -func TestTrimK8sVersion(t *testing.T) { - tests := []struct { - name string - in string - want string - }{ - { - name: "1.18.0", - in: "1.18.0", - want: "1.18.0", - }, - { - name: "v1.18.0", - in: "v1.18.0", - want: "1.18.0", - }, - { - name: "v1.18.0+", - in: "v1.18.0+", - want: "1.18.0", - }, - { - name: "v1.18.0+123", - in: "v1.18.0+123", - want: "1.18.0", - }, - { - name: "v1.18.0+123-abc", - in: "v1.18.0+123-abc", - want: "1.18.0", - }, - { - name: "v1.18.0-abc", - in: "v1.18.0-abc", - want: "1.18.0", - }, - { - name: "v1.18.0-abc+123", - in: "v1.18.0-abc+123", - want: "1.18.0", - }, - { - name: "v1.18.0-abc+123-def", - in: "v1.18.0-abc+123-def", - want: "1.18.0", - }, - { - name: "v1.18.0-abc+123-def+456", - in: "v1.18.0-abc+123-def+456", - want: "1.18.0", - }, - { - name: "v1.18.0-abc+123-def+456-ghi", - in: "v1.18.0-abc+123-def+456-ghi", - want: "1.18.0", - }, - { - name: "v1.18.0-abc+123-def+456-ghi+jkl", - in: "v1.18.0-abc+123-def+456-ghi+jkl", - want: "1.18.0", - }, - { - name: "1.18.0-abc+123-def+456-ghi+jkl", - in: "1.18.0-abc+123-def+456-ghi+jkl", - want: "1.18.0", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := trimK8sVersion(tt.in); got != tt.want { - t.Errorf("trimK8sVersion() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/cmd/gitops/remove/cmd.go b/cmd/gitops/remove/cmd.go deleted file mode 100644 index 35c666f3b1..0000000000 --- a/cmd/gitops/remove/cmd.go +++ /dev/null @@ -1,18 +0,0 @@ -package remove - -import ( - "github.com/spf13/cobra" - "github.com/weaveworks/weave-gitops/cmd/gitops/config" - "github.com/weaveworks/weave-gitops/cmd/gitops/remove/run" -) - -func GetCommand(opts *config.Options) *cobra.Command { - cmd := &cobra.Command{ - Use: "remove", - Short: "Remove various components of Weave GitOps", - } - - cmd.AddCommand(run.RunCommand(opts)) - - return cmd -} diff --git a/cmd/gitops/remove/run/cmd.go b/cmd/gitops/remove/run/cmd.go deleted file mode 100644 index 1fb1b1a62b..0000000000 --- a/cmd/gitops/remove/run/cmd.go +++ /dev/null @@ -1,202 +0,0 @@ -package run - -import ( - "context" - "fmt" - "os" - - "github.com/spf13/cobra" - "github.com/weaveworks/weave-gitops/cmd/gitops/cmderrors" - "github.com/weaveworks/weave-gitops/cmd/gitops/config" - "github.com/weaveworks/weave-gitops/pkg/kube" - "github.com/weaveworks/weave-gitops/pkg/logger" - "github.com/weaveworks/weave-gitops/pkg/run" - "github.com/weaveworks/weave-gitops/pkg/run/install" - "github.com/weaveworks/weave-gitops/pkg/run/session" - "github.com/weaveworks/weave-gitops/pkg/run/watch" - "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/client-go/rest" -) - -type RunCommandFlags struct { - AllSessions bool - NoSession bool - - // Global flags. - Namespace string - KubeConfig string - - // Flags, created by genericclioptions. - Context string -} - -var flags RunCommandFlags - -var kubeConfigArgs *genericclioptions.ConfigFlags - -func RunCommand(opts *config.Options) *cobra.Command { - cmd := &cobra.Command{ - Use: "run", - Short: "Remove GitOps Run sessions", - Long: "Remove GitOps Run sessions", - Example: ` -# Remove the GitOps Run session "dev-1234" from the "flux-system" namespace -gitops remove run --namespace flux-system dev-1234 - -# Remove all GitOps Run sessions from the default namespace -gitops remove run --all-sessions - -# Remove all GitOps Run sessions from the dev namespace -gitops remove run -n dev --all-sessions - -# Clean up resources from a failed GitOps Run in no session mode -gitops remove run --no-session -`, - PreRunE: removeRunPreRunE(opts), - RunE: removeRunRunE(opts), - - SilenceUsage: true, - SilenceErrors: true, - DisableAutoGenTag: true, - } - - cmdFlags := cmd.Flags() - - cmdFlags.BoolVar(&flags.AllSessions, "all-sessions", false, "Remove all GitOps Run sessions") - cmdFlags.BoolVar(&flags.NoSession, "no-session", false, "Remove all GitOps Run components in the non-session mode") - - kubeConfigArgs = run.GetKubeConfigArgs() - - kubeConfigArgs.AddFlags(cmd.Flags()) - - return cmd -} - -func getKubeClient(cmd *cobra.Command) (*kube.KubeHTTP, *rest.Config, error) { - var err error - - log := logger.NewCLILogger(os.Stdout) - - if flags.Namespace, err = cmd.Flags().GetString("namespace"); err != nil { - return nil, nil, err - } - - kubeConfigArgs.Namespace = &flags.Namespace - - if flags.KubeConfig, err = cmd.Flags().GetString("kubeconfig"); err != nil { - return nil, nil, err - } - - if flags.Context, err = cmd.Flags().GetString("context"); err != nil { - return nil, nil, err - } - - if flags.KubeConfig != "" { - kubeConfigArgs.KubeConfig = &flags.KubeConfig - - if flags.Context == "" { - log.Failuref("A context should be provided if a kubeconfig is provided") - return nil, nil, cmderrors.ErrNoContextForKubeConfig - } - } - - var contextName string - - if flags.Context != "" { - contextName = flags.Context - } else { - _, contextName, err = kube.RestConfig() - if err != nil { - log.Failuref("Error getting a restconfig: %v", err.Error()) - return nil, nil, cmderrors.ErrNoCluster - } - } - - cfg, err := kubeConfigArgs.ToRESTConfig() - if err != nil { - return nil, nil, fmt.Errorf("error getting a restconfig from kube config args: %w", err) - } - - kubeClientOpts := run.GetKubeClientOptions() - kubeClientOpts.BindFlags(cmd.Flags()) - - kubeClient, err := run.GetKubeClient(log, contextName, cfg, kubeClientOpts) - if err != nil { - return nil, nil, cmderrors.ErrGetKubeClient - } - - return kubeClient, cfg, nil -} - -func removeRunPreRunE(opts *config.Options) func(cmd *cobra.Command, args []string) error { - return func(cmd *cobra.Command, args []string) error { - // if flags.NoSession is set, we don't need to check for session name - if flags.NoSession { - return nil - } - - numArgs := len(args) - if numArgs == 0 && !flags.AllSessions { - return cmderrors.ErrSessionNameIsRequired - } - - return nil - } -} - -func removeRunRunE(opts *config.Options) func(cmd *cobra.Command, args []string) error { - return func(cmd *cobra.Command, args []string) error { - kubeClient, _, err := getKubeClient(cmd) - if err != nil { - return err - } - - log := logger.NewCLILogger(os.Stdout) - ctx := context.Background() - - if flags.NoSession { - if err := watch.CleanupBucketSourceAndHelm(ctx, log, kubeClient, flags.Namespace); err != nil { - return err - } - - if err := watch.CleanupBucketSourceAndKS(ctx, log, kubeClient, flags.Namespace); err != nil { - return err - } - - if err := watch.UninstallDevBucketServer(ctx, log, kubeClient); err != nil { - return err - } - - if err := install.UninstallFluentBit(ctx, log, kubeClient, flags.Namespace, install.FluentBitHRName); err != nil { - return err - } - } else if flags.AllSessions { - internalSessions, listErr := session.List(kubeClient, flags.Namespace) - if listErr != nil { - return listErr - } - - for _, internalSession := range internalSessions { - log.Actionf("Removing session %s/%s ...", internalSession.SessionNamespace, internalSession.SessionName) - - if err := session.Remove(kubeClient, internalSession); err != nil { - return err - } - - log.Successf("Session %s/%s was successfully removed.", internalSession.SessionNamespace, internalSession.SessionName) - } - } else { - internalSession, err := session.Get(kubeClient, args[0], flags.Namespace) - if err != nil { - return err - } - log.Actionf("Removing session %s/%s ...", internalSession.SessionNamespace, internalSession.SessionName) - if err := session.Remove(kubeClient, internalSession); err != nil { - return err - } - log.Successf("Session %s/%s was successfully removed.", internalSession.SessionNamespace, internalSession.SessionName) - } - - return nil - } -} diff --git a/cmd/gitops/root/cmd.go b/cmd/gitops/root/cmd.go index 1161b5fd68..cc7cc4f42c 100644 --- a/cmd/gitops/root/cmd.go +++ b/cmd/gitops/root/cmd.go @@ -7,25 +7,20 @@ import ( "strings" "time" - "github.com/weaveworks/weave-gitops/cmd/gitops/logs" - "github.com/weaveworks/weave-gitops/cmd/gitops/replan" - "github.com/weaveworks/weave-gitops/cmd/gitops/resume" - "github.com/weaveworks/weave-gitops/cmd/gitops/suspend" - - "github.com/weaveworks/weave-gitops/cmd/gitops/remove" - "github.com/manifoldco/promptui" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/weaveworks/weave-gitops/cmd/gitops/beta" - "github.com/weaveworks/weave-gitops/cmd/gitops/beta/run" "github.com/weaveworks/weave-gitops/cmd/gitops/check" cfg "github.com/weaveworks/weave-gitops/cmd/gitops/config" "github.com/weaveworks/weave-gitops/cmd/gitops/create" deletepkg "github.com/weaveworks/weave-gitops/cmd/gitops/delete" "github.com/weaveworks/weave-gitops/cmd/gitops/docs" "github.com/weaveworks/weave-gitops/cmd/gitops/get" + "github.com/weaveworks/weave-gitops/cmd/gitops/logs" + "github.com/weaveworks/weave-gitops/cmd/gitops/replan" + "github.com/weaveworks/weave-gitops/cmd/gitops/resume" "github.com/weaveworks/weave-gitops/cmd/gitops/set" + "github.com/weaveworks/weave-gitops/cmd/gitops/suspend" "github.com/weaveworks/weave-gitops/cmd/gitops/version" "github.com/weaveworks/weave-gitops/pkg/analytics" "github.com/weaveworks/weave-gitops/pkg/config" @@ -159,14 +154,11 @@ func RootCmd() *cobra.Command { rootCmd.AddCommand(set.SetCommand(options)) rootCmd.AddCommand(docs.Cmd) rootCmd.AddCommand(check.Cmd) - rootCmd.AddCommand(beta.GetCommand(options)) rootCmd.AddCommand(create.GetCommand(options)) rootCmd.AddCommand(deletepkg.GetCommand(options)) rootCmd.AddCommand(logs.GetCommand(options)) - rootCmd.AddCommand(remove.GetCommand(options)) rootCmd.AddCommand(replan.Command(options)) rootCmd.AddCommand(resume.Command(options)) - rootCmd.AddCommand(run.RunCommand(options)) rootCmd.AddCommand(suspend.Command(options)) return rootCmd