diff --git a/cmd/apprepository-controller/Dockerfile b/cmd/apprepository-controller/Dockerfile index 23e7bf583a4..65180c0b00f 100644 --- a/cmd/apprepository-controller/Dockerfile +++ b/cmd/apprepository-controller/Dockerfile @@ -5,11 +5,12 @@ WORKDIR /go/src/github.com/kubeapps/kubeapps COPY go.mod go.sum ./ COPY pkg pkg COPY cmd cmd +ARG VERSION # With the trick below, Go's build cache is kept between builds. # https://github.com/golang/go/issues/27719#issuecomment-514747274 RUN --mount=type=cache,target=/go/pkg/mod \ - --mount=type=cache,target=/root/.cache/go-build \ - CGO_ENABLED=0 go build -installsuffix cgo ./cmd/apprepository-controller + --mount=type=cache,target=/root/.cache/go-build \ + CGO_ENABLED=0 go build -installsuffix cgo -ldflags "-X github.com/kubeapps/kubeapps/cmd/apprepository-controller/cmd.version=$VERSION" ./cmd/apprepository-controller FROM scratch COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ diff --git a/cmd/apprepository-controller/cmd/root.go b/cmd/apprepository-controller/cmd/root.go new file mode 100644 index 00000000000..530bd93dd7e --- /dev/null +++ b/cmd/apprepository-controller/cmd/root.go @@ -0,0 +1,127 @@ +/* +Copyright 2021 VMware. 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. +*/ + +package cmd + +import ( + "fmt" + "os" + + "github.com/kubeapps/kubeapps/cmd/apprepository-controller/server" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + corev1 "k8s.io/api/core/v1" + log "k8s.io/klog/v2" +) + +var ( + cfgFile string + serveOpts server.Config + // This Version var is updated during the build + // see the -ldflags option in the cmd/apprepository-controller/Dockerfile + version = "devel" +) + +// rootCmd represents the base command when called without any subcommands +var rootCmd *cobra.Command + +func newRootCmd() *cobra.Command { + return &cobra.Command{ + Use: "apprepository-controller", + Short: "Apprepository-controller is a Kubernetes controller for managing package repositories added to Kubeapps.", + PreRun: func(cmd *cobra.Command, args []string) { + log.Infof("apprepository-controller has been configured with: %#v", serveOpts) + }, + RunE: func(cmd *cobra.Command, args []string) error { + + return server.Serve(serveOpts) + }, + Version: "devel", + } +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + cobra.CheckErr(rootCmd.Execute()) +} + +func init() { + cobra.OnInitialize(initConfig) + rootCmd = newRootCmd() + rootCmd.SetVersionTemplate(version) + setFlags(rootCmd) + serveOpts.ImagePullSecretsRefs = getImagePullSecretsRefs(serveOpts.RepoSyncImagePullSecrets) +} + +func setFlags(c *cobra.Command) { + c.Flags().StringVar(&serveOpts.Kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") + c.Flags().StringVar(&serveOpts.MasterURL, "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.") + c.Flags().StringVar(&serveOpts.RepoSyncImage, "repo-sync-image", "docker.io/kubeapps/asset-syncer:latest", "container repo/image to use in CronJobs") + c.Flags().StringSliceVar(&serveOpts.RepoSyncImagePullSecrets, "repo-sync-image-pullsecrets", nil, "optional reference to secrets in the same namespace to use for pulling the image used by this pod") + c.Flags().StringVar(&serveOpts.RepoSyncCommand, "repo-sync-cmd", "/chart-repo", "command used to sync/delete repos for repo-sync-image") + c.Flags().StringVar(&serveOpts.KubeappsNamespace, "namespace", "kubeapps", "Namespace to discover AppRepository resources") + c.Flags().BoolVar(&serveOpts.ReposPerNamespace, "repos-per-namespace", true, "Defaults to watch for repos in all namespaces. Switch to false to watch only the configured namespace.") + c.Flags().StringVar(&serveOpts.DBURL, "database-url", "localhost", "Database URL") + c.Flags().StringVar(&serveOpts.DBUser, "database-user", "root", "Database user") + c.Flags().StringVar(&serveOpts.DBName, "database-name", "charts", "Database name") + c.Flags().StringVar(&serveOpts.DBSecretName, "database-secret-name", "kubeapps-db", "Kubernetes secret name for database credentials") + c.Flags().StringVar(&serveOpts.DBSecretKey, "database-secret-key", "postgresql-root-password", "Kubernetes secret key used for database credentials") + c.Flags().StringVar(&serveOpts.UserAgentComment, "user-agent-comment", "", "UserAgent comment used during outbound requests") + c.Flags().StringVar(&serveOpts.Crontab, "crontab", "*/10 * * * *", "CronTab to specify schedule") + // TTLSecondsAfterFinished specifies the number of seconds a sync job should live after finishing. + // The support for this is currently beta in K8s (v1.21), older versions require a feature gate being set to enable it. + // See https://kubernetes.io/docs/concepts/workloads/controllers/job/#clean-up-finished-jobs-automatically + c.Flags().StringVar(&serveOpts.TTLSecondsAfterFinished, "ttl-lifetime-afterfinished-job", "3600", "Lifetime limit after which the resource Jobs are deleted expressed in seconds by default is 3600 (1h) ") + +} + +// initConfig reads in config file and ENV variables if set. +func initConfig() { + if cfgFile != "" { + // Use config file from the flag. + viper.SetConfigFile(cfgFile) + } else { + // Find home directory. + home, err := os.UserHomeDir() + cobra.CheckErr(err) + + // Search config in home directory with name ".kubeops" (without extension). + viper.AddConfigPath(home) + viper.SetConfigType("yaml") + viper.SetConfigName(".kubeops") + } + + viper.AutomaticEnv() // read in environment variables that match + + // If a config file is found, read it in. + if err := viper.ReadInConfig(); err == nil { + fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) + } +} + +// getImagePullSecretsRefs gets the []string of Secrets names from the +// StringSliceVar flag list passed in the repoSyncImagePullSecrets arg +func getImagePullSecretsRefs(imagePullSecretsRefsArr []string) []corev1.LocalObjectReference { + var imagePullSecretsRefs []corev1.LocalObjectReference + + // getting and appending a []LocalObjectReference for each ImagePullSecret passed + for _, imagePullSecretName := range imagePullSecretsRefsArr { + imagePullSecretsRefs = append(imagePullSecretsRefs, corev1.LocalObjectReference{Name: imagePullSecretName}) + } + return imagePullSecretsRefs +} diff --git a/cmd/apprepository-controller/main_test.go b/cmd/apprepository-controller/cmd/root_test.go similarity index 70% rename from cmd/apprepository-controller/main_test.go rename to cmd/apprepository-controller/cmd/root_test.go index c43111241c7..11b3321498e 100644 --- a/cmd/apprepository-controller/main_test.go +++ b/cmd/apprepository-controller/cmd/root_test.go @@ -1,10 +1,29 @@ -package main +/* +Copyright 2021 VMware. 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. +*/ + +package cmd import ( + "bytes" "strings" + "testing" "github.com/google/go-cmp/cmp" + "github.com/kubeapps/kubeapps/cmd/apprepository-controller/server" v1 "k8s.io/api/core/v1" ) @@ -12,15 +31,15 @@ func TestParseFlagsCorrect(t *testing.T) { var tests = []struct { name string args []string - conf Config + conf server.Config }{ { "no arguments returns default flag values", []string{}, - Config{ + server.Config{ Kubeconfig: "", MasterURL: "", - RepoSyncImage: "quay.io/helmpack/chart-repo:latest", + RepoSyncImage: "docker.io/kubeapps/asset-syncer:latest", RepoSyncImagePullSecrets: nil, RepoSyncCommand: "/chart-repo", KubeappsNamespace: "kubeapps", @@ -33,7 +52,6 @@ func TestParseFlagsCorrect(t *testing.T) { UserAgentComment: "", Crontab: "*/10 * * * *", TTLSecondsAfterFinished: "3600", - Args: []string{}, }, }, { @@ -42,10 +60,10 @@ func TestParseFlagsCorrect(t *testing.T) { "--repo-sync-image-pullsecrets=s1, s2", "--repo-sync-image-pullsecrets= s3", }, - Config{ + server.Config{ Kubeconfig: "", MasterURL: "", - RepoSyncImage: "quay.io/helmpack/chart-repo:latest", + RepoSyncImage: "docker.io/kubeapps/asset-syncer:latest", RepoSyncImagePullSecrets: []string{"s1", " s2", " s3"}, ImagePullSecretsRefs: []v1.LocalObjectReference{{Name: "s1"}, {Name: " s2"}, {Name: " s3"}}, RepoSyncCommand: "/chart-repo", @@ -59,7 +77,6 @@ func TestParseFlagsCorrect(t *testing.T) { UserAgentComment: "", Crontab: "*/10 * * * *", TTLSecondsAfterFinished: "3600", - Args: []string{}, }, }, { @@ -81,7 +98,7 @@ func TestParseFlagsCorrect(t *testing.T) { "--user-agent-comment", "foo11", "--crontab", "foo12", }, - Config{ + server.Config{ Kubeconfig: "foo01", MasterURL: "foo02", RepoSyncImage: "foo03", @@ -98,23 +115,21 @@ func TestParseFlagsCorrect(t *testing.T) { UserAgentComment: "foo11", Crontab: "foo12", TTLSecondsAfterFinished: "3600", - Args: []string{}, }, }, } for _, tt := range tests { - t.Run(strings.Join(tt.args, " "), func(t *testing.T) { - conf, output, err := parseFlags("program", tt.args) - conf.ImagePullSecretsRefs = getImagePullSecretsRefs(conf.RepoSyncImagePullSecrets) - - if err != nil { - t.Errorf("err got:\n%v\nwant nil", err) - } - if output != "" { - t.Errorf("output got:\n%q\nwant empty", output) - } - if got, want := *conf, tt.conf; !cmp.Equal(want, got) { + t.Run(tt.name, func(t *testing.T) { + cmd := newRootCmd() + b := bytes.NewBufferString("") + cmd.SetOut(b) + cmd.SetErr(b) + setFlags(cmd) + cmd.SetArgs(tt.args) + cmd.Execute() + serveOpts.ImagePullSecretsRefs = getImagePullSecretsRefs(serveOpts.RepoSyncImagePullSecrets) + if got, want := serveOpts, tt.conf; !cmp.Equal(want, got) { t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(want, got)) } }) @@ -140,12 +155,15 @@ func TestParseFlagsError(t *testing.T) { } for _, tt := range tests { - t.Run(strings.Join(tt.args, " "), func(t *testing.T) { - conf, _, err := parseFlags("prog", tt.args) - if conf != nil { - t.Errorf("conf got %v, want nil", conf) - } - if strings.Index(err.Error(), tt.errstr) < 0 { + t.Run(tt.name, func(t *testing.T) { + cmd := newRootCmd() + b := bytes.NewBufferString("") + cmd.SetOut(b) + cmd.SetErr(b) + setFlags(cmd) + cmd.SetArgs(tt.args) + err := cmd.Execute() + if !strings.Contains(err.Error(), tt.errstr) { t.Errorf("err got %q, want to find %q", err.Error(), tt.errstr) } }) diff --git a/cmd/apprepository-controller/hack/custom-boilerplate.go.txt b/cmd/apprepository-controller/hack/custom-boilerplate.go.txt index c329d8c0e25..ba8ae7dfd93 100644 --- a/cmd/apprepository-controller/hack/custom-boilerplate.go.txt +++ b/cmd/apprepository-controller/hack/custom-boilerplate.go.txt @@ -1,5 +1,5 @@ /* -Copyright YEAR Bitnami. +Copyright 2021 VMware. 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. @@ -13,4 +13,3 @@ 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. */ - diff --git a/cmd/apprepository-controller/main.go b/cmd/apprepository-controller/main.go index 200b2a829f1..7ee8ef589a7 100644 --- a/cmd/apprepository-controller/main.go +++ b/cmd/apprepository-controller/main.go @@ -1,5 +1,5 @@ /* -Copyright 2017 Bitnami. +Copyright 2021 VMware. 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. @@ -16,159 +16,8 @@ limitations under the License. package main -import ( - "bytes" - "os" - - clientset "github.com/kubeapps/kubeapps/cmd/apprepository-controller/pkg/client/clientset/versioned" - informers "github.com/kubeapps/kubeapps/cmd/apprepository-controller/pkg/client/informers/externalversions" - "github.com/kubeapps/kubeapps/cmd/apprepository-controller/pkg/signals" - corev1 "k8s.io/api/core/v1" - kubeinformers "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" // Uncomment the following line to load the gcp plugin (only required to authenticate against GKE clusters). - - // _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" - log "github.com/sirupsen/logrus" - flag "github.com/spf13/pflag" -) - -// Config contains all the flags passed through the command line -// besides, it contains the []LocalObjectReference for the PullSecrets -// so they can be shared across the jobs -type Config struct { - MasterURL string - Kubeconfig string - RepoSyncImage string - RepoSyncImagePullSecrets []string - ImagePullSecretsRefs []corev1.LocalObjectReference - RepoSyncCommand string - KubeappsNamespace string - DBURL string - DBUser string - DBName string - DBSecretName string - DBSecretKey string - UserAgentComment string - Crontab string - TTLSecondsAfterFinished string - ReposPerNamespace bool - - // Args are the positional (non-flag) command-line arguments. - Args []string -} - -// parseFlags parses the command-line arguments provided to the program. -// Typically os.Args[0] is provided as 'programName' and os.args[1:] as 'args'. -func parseFlags(progname string, args []string) (config *Config, output string, err error) { - flagSet := flag.NewFlagSet(progname, flag.ContinueOnError) - var buf bytes.Buffer - flagSet.SetOutput(&buf) - - var conf Config - - flagSet.StringVar(&conf.Kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") - flagSet.StringVar(&conf.MasterURL, "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.") - flagSet.StringVar(&conf.RepoSyncImage, "repo-sync-image", "quay.io/helmpack/chart-repo:latest", "container repo/image to use in CronJobs") - flagSet.StringSliceVar(&conf.RepoSyncImagePullSecrets, "repo-sync-image-pullsecrets", nil, "optional reference to secrets in the same namespace to use for pulling the image used by this pod") - flagSet.StringVar(&conf.RepoSyncCommand, "repo-sync-cmd", "/chart-repo", "command used to sync/delete repos for repo-sync-image") - flagSet.StringVar(&conf.KubeappsNamespace, "namespace", "kubeapps", "Namespace to discover AppRepository resources") - flagSet.BoolVar(&conf.ReposPerNamespace, "repos-per-namespace", true, "Defaults to watch for repos in all namespaces. Switch to false to watch only the configured namespace.") - flagSet.StringVar(&conf.DBURL, "database-url", "localhost", "Database URL") - flagSet.StringVar(&conf.DBUser, "database-user", "root", "Database user") - flagSet.StringVar(&conf.DBName, "database-name", "charts", "Database name") - flagSet.StringVar(&conf.DBSecretName, "database-secret-name", "kubeapps-db", "Kubernetes secret name for database credentials") - flagSet.StringVar(&conf.DBSecretKey, "database-secret-key", "postgresql-root-password", "Kubernetes secret key used for database credentials") - flagSet.StringVar(&conf.UserAgentComment, "user-agent-comment", "", "UserAgent comment used during outbound requests") - flagSet.StringVar(&conf.Crontab, "crontab", "*/10 * * * *", "CronTab to specify schedule") - // TTLSecondsAfterFinished specifies the number of seconds a sync job should live after finishing. - // The support for this is currently alpha in K8s itself, requiring a feature gate being set to enable - // it. See https://kubernetes.io/docs/concepts/workloads/controllers/job/#clean-up-finished-jobs-automatically - flagSet.StringVar(&conf.TTLSecondsAfterFinished, "ttl-lifetime-afterfinished-job", "3600", "Lifetime limit after which the resource Jobs are deleted expressed in seconds by default is 3600 (1h) ") - - err = flagSet.Parse(args) - if err != nil { - return nil, buf.String(), err - } - conf.Args = flagSet.Args() - return &conf, buf.String(), nil -} +import "github.com/kubeapps/kubeapps/cmd/apprepository-controller/cmd" func main() { - conf, output, err := parseFlags(os.Args[0], os.Args[1:]) - - if err != nil { - log.Fatal("error parsing command-line arguments: ", err) - log.Fatal(output) - log.Exit(1) - } - - log.WithFields(log.Fields{ - "kubeconfig": conf.Kubeconfig, - "master": conf.MasterURL, - "repo-sync-image": conf.RepoSyncImage, - "repo-sync-image-pullsecrets": conf.RepoSyncImagePullSecrets, - "repo-sync-cmd": conf.RepoSyncCommand, - "namespace": conf.KubeappsNamespace, - "repos-per-namespace": conf.ReposPerNamespace, - "database-url": conf.DBURL, - "database-user": conf.DBUser, - "database-name": conf.DBName, - "database-secret-name": conf.DBSecretName, - "database-secret-key": conf.DBSecretKey, - "user-agent-comment": conf.UserAgentComment, - "crontab": conf.Crontab, - "ttl-lifetime-afterfinished-job": conf.TTLSecondsAfterFinished, - }).Info("apprepository-controller configured with these args:") - - // set up signals so we handle the first shutdown signal gracefully - stopCh := signals.SetupSignalHandler() - - cfg, err := clientcmd.BuildConfigFromFlags(conf.MasterURL, conf.Kubeconfig) - if err != nil { - log.Fatalf("Error building kubeconfig: %s", err.Error()) - } - - kubeClient, err := kubernetes.NewForConfig(cfg) - if err != nil { - log.Fatalf("Error building kubernetes clientset: %s", err.Error()) - } - - apprepoClient, err := clientset.NewForConfig(cfg) - if err != nil { - log.Fatalf("Error building apprepo clientset: %s", err.Error()) - } - - // We're interested in being informed about cronjobs in kubeapps namespace only, currently. - kubeInformerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 0, kubeinformers.WithNamespace(conf.KubeappsNamespace)) - // Enable app repo scanning to be manually set to scan the kubeapps repo only. See #1923. - var apprepoInformerFactory informers.SharedInformerFactory - if conf.ReposPerNamespace { - apprepoInformerFactory = informers.NewSharedInformerFactory(apprepoClient, 0) - } else { - apprepoInformerFactory = informers.NewFilteredSharedInformerFactory(apprepoClient, 0, conf.KubeappsNamespace, nil) - } - - conf.ImagePullSecretsRefs = getImagePullSecretsRefs(conf.RepoSyncImagePullSecrets) - - controller := NewController(kubeClient, apprepoClient, kubeInformerFactory, apprepoInformerFactory, conf) - - go kubeInformerFactory.Start(stopCh) - go apprepoInformerFactory.Start(stopCh) - - if err = controller.Run(2, stopCh); err != nil { - log.Fatalf("Error running controller: %s", err.Error()) - } -} - -// getImagePullSecretsRefs gets the []string of Secrets names from the -// StringSliceVar flag list passed in the repoSyncImagePullSecrets arg -func getImagePullSecretsRefs(imagePullSecretsRefsArr []string) []corev1.LocalObjectReference { - var imagePullSecretsRefs []corev1.LocalObjectReference - - // getting and appending a []LocalObjectReference for each ImagePullSecret passed - for _, imagePullSecretName := range imagePullSecretsRefsArr { - imagePullSecretsRefs = append(imagePullSecretsRefs, corev1.LocalObjectReference{Name: imagePullSecretName}) - } - return imagePullSecretsRefs + cmd.Execute() } diff --git a/cmd/apprepository-controller/pkg/apis/apprepository/v1alpha1/zz_generated.deepcopy.go b/cmd/apprepository-controller/pkg/apis/apprepository/v1alpha1/zz_generated.deepcopy.go index d75dc193a67..079e0bbaf70 100644 --- a/cmd/apprepository-controller/pkg/apis/apprepository/v1alpha1/zz_generated.deepcopy.go +++ b/cmd/apprepository-controller/pkg/apis/apprepository/v1alpha1/zz_generated.deepcopy.go @@ -1,3 +1,4 @@ +//go:build !ignore_autogenerated // +build !ignore_autogenerated /* diff --git a/cmd/apprepository-controller/pkg/signals/signal_posix.go b/cmd/apprepository-controller/pkg/signals/signal_posix.go index 07685590873..d180faf3b04 100644 --- a/cmd/apprepository-controller/pkg/signals/signal_posix.go +++ b/cmd/apprepository-controller/pkg/signals/signal_posix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows /* diff --git a/cmd/apprepository-controller/controller.go b/cmd/apprepository-controller/server/controller.go similarity index 99% rename from cmd/apprepository-controller/controller.go rename to cmd/apprepository-controller/server/controller.go index 10120202b09..3da2aab56f0 100644 --- a/cmd/apprepository-controller/controller.go +++ b/cmd/apprepository-controller/server/controller.go @@ -1,5 +1,5 @@ /* -Copyright 2017 Bitnami. +Copyright 2021 VMware. 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. @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package main +package server import ( "context" diff --git a/cmd/apprepository-controller/controller_test.go b/cmd/apprepository-controller/server/controller_test.go similarity index 98% rename from cmd/apprepository-controller/controller_test.go rename to cmd/apprepository-controller/server/controller_test.go index d6e6f0adee5..824037d6962 100644 --- a/cmd/apprepository-controller/controller_test.go +++ b/cmd/apprepository-controller/server/controller_test.go @@ -1,4 +1,20 @@ -package main +/* +Copyright 2021 VMware. 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. +*/ + +package server import ( "testing" diff --git a/cmd/apprepository-controller/server/server.go b/cmd/apprepository-controller/server/server.go new file mode 100644 index 00000000000..a53579330c9 --- /dev/null +++ b/cmd/apprepository-controller/server/server.go @@ -0,0 +1,88 @@ +/* +Copyright 2021 VMware. 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. +*/ + +package server + +import ( + "fmt" + + clientset "github.com/kubeapps/kubeapps/cmd/apprepository-controller/pkg/client/clientset/versioned" + informers "github.com/kubeapps/kubeapps/cmd/apprepository-controller/pkg/client/informers/externalversions" + "github.com/kubeapps/kubeapps/cmd/apprepository-controller/pkg/signals" + corev1 "k8s.io/api/core/v1" + kubeinformers "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" // Uncomment the following line to load the gcp plugin (only required to authenticate against GKE clusters). +) + +type Config struct { + MasterURL string + Kubeconfig string + RepoSyncImage string + RepoSyncImagePullSecrets []string + ImagePullSecretsRefs []corev1.LocalObjectReference + RepoSyncCommand string + KubeappsNamespace string + DBURL string + DBUser string + DBName string + DBSecretName string + DBSecretKey string + UserAgentComment string + Crontab string + TTLSecondsAfterFinished string + ReposPerNamespace bool +} + +func Serve(serveOpts Config) error { + cfg, err := clientcmd.BuildConfigFromFlags(serveOpts.MasterURL, serveOpts.Kubeconfig) + if err != nil { + return fmt.Errorf("Error building kubeconfig: %s", err.Error()) + } + + kubeClient, err := kubernetes.NewForConfig(cfg) + if err != nil { + return fmt.Errorf("Error building kubernetes clientset: %s", err.Error()) + } + + apprepoClient, err := clientset.NewForConfig(cfg) + if err != nil { + return fmt.Errorf("Error building apprepo clientset: %s", err.Error()) + } + + // set up signals so we handle the first shutdown signal gracefully + stopCh := signals.SetupSignalHandler() + + // We're interested in being informed about cronjobs in kubeapps namespace only, currently. + kubeInformerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 0, kubeinformers.WithNamespace(serveOpts.KubeappsNamespace)) + // Enable app repo scanning to be manually set to scan the kubeapps repo only. See #1923. + var apprepoInformerFactory informers.SharedInformerFactory + if serveOpts.ReposPerNamespace { + apprepoInformerFactory = informers.NewSharedInformerFactory(apprepoClient, 0) + } else { + apprepoInformerFactory = informers.NewFilteredSharedInformerFactory(apprepoClient, 0, serveOpts.KubeappsNamespace, nil) + } + + controller := NewController(kubeClient, apprepoClient, kubeInformerFactory, apprepoInformerFactory, &serveOpts) + + go kubeInformerFactory.Start(stopCh) + go apprepoInformerFactory.Start(stopCh) + + if err = controller.Run(2, stopCh); err != nil { + return fmt.Errorf("Error running controller: %s", err.Error()) + } + return nil +} diff --git a/docs/developer/apprepository-controller.md b/docs/developer/apprepository-controller.md index 098db05aaef..8a47b30d685 100644 --- a/docs/developer/apprepository-controller.md +++ b/docs/developer/apprepository-controller.md @@ -51,7 +51,7 @@ kubectl -n kubeapps scale deployment kubeapps-internal-apprepository-controller You can now execute the `apprepository-controller` binary on the developer host with: ```bash -./apprepository-controller --repo-sync-image=quay.io/helmpack/chart-repo:myver --kubeconfig ~/.kube/config +./apprepository-controller --repo-sync-image=docker.io/kubeapps/asset-syncer:myver --kubeconfig ~/.kube/config ``` Performing application repository actions in the Kubeapps dashboard will now trigger operations in the `apprepository-controller` binary running locally on your development host.