From 818d41b39496111d6b699c60fc986e4ace10d501 Mon Sep 17 00:00:00 2001 From: "Roland.Ma" Date: Fri, 27 Aug 2021 06:27:38 +0000 Subject: [PATCH] add helm operator sdk and gateway helm chart Signed-off-by: Roland.Ma --- cmd/controller-manager/app/options/options.go | 5 +- cmd/controller-manager/app/server.go | 10 + .../crds/gateway.kubesphere.io_gateways.yaml | 92 ++++++ .../crds/gateway.kubesphere.io_nginxes.yaml | 44 +++ config/gateway/.helmignore | 23 ++ config/gateway/Chart.yaml | 6 + config/gateway/templates/nginx-ingress.yaml | 272 ++++++++++++++++++ config/gateway/values.yaml | 25 ++ pkg/apiserver/config/config.go | 3 + pkg/apiserver/config/config_test.go | 5 + pkg/controller/helm/helm_controller.go | 78 +++++ pkg/controller/helm/helm_controller_test.go | 89 ++++++ pkg/simple/client/gateway/options.go | 62 ++++ 13 files changed, 713 insertions(+), 1 deletion(-) create mode 100644 config/crds/gateway.kubesphere.io_gateways.yaml create mode 100644 config/crds/gateway.kubesphere.io_nginxes.yaml create mode 100644 config/gateway/.helmignore create mode 100644 config/gateway/Chart.yaml create mode 100644 config/gateway/templates/nginx-ingress.yaml create mode 100644 config/gateway/values.yaml create mode 100644 pkg/controller/helm/helm_controller.go create mode 100644 pkg/controller/helm/helm_controller_test.go create mode 100644 pkg/simple/client/gateway/options.go diff --git a/cmd/controller-manager/app/options/options.go b/cmd/controller-manager/app/options/options.go index 06f854da8..a102f7025 100644 --- a/cmd/controller-manager/app/options/options.go +++ b/cmd/controller-manager/app/options/options.go @@ -30,6 +30,7 @@ import ( authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" "kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins" + "kubesphere.io/kubesphere/pkg/simple/client/gateway" "kubesphere.io/kubesphere/pkg/simple/client/k8s" ldapclient "kubesphere.io/kubesphere/pkg/simple/client/ldap" "kubesphere.io/kubesphere/pkg/simple/client/multicluster" @@ -49,6 +50,7 @@ type KubeSphereControllerManagerOptions struct { NetworkOptions *network.Options MultiClusterOptions *multicluster.Options ServiceMeshOptions *servicemesh.Options + GatewayOptions *gateway.Options LeaderElect bool LeaderElection *leaderelection.LeaderElectionConfig WebhookCertDir string @@ -74,6 +76,7 @@ func NewKubeSphereControllerManagerOptions() *KubeSphereControllerManagerOptions MultiClusterOptions: multicluster.NewOptions(), ServiceMeshOptions: servicemesh.NewServiceMeshOptions(), AuthenticationOptions: authoptions.NewAuthenticateOptions(), + GatewayOptions: gateway.NewGatewayOptions(), LeaderElection: &leaderelection.LeaderElectionConfig{ LeaseDuration: 30 * time.Second, RenewDeadline: 15 * time.Second, @@ -99,7 +102,7 @@ func (s *KubeSphereControllerManagerOptions) Flags() cliflag.NamedFlagSets { s.NetworkOptions.AddFlags(fss.FlagSet("network"), s.NetworkOptions) s.MultiClusterOptions.AddFlags(fss.FlagSet("multicluster"), s.MultiClusterOptions) s.ServiceMeshOptions.AddFlags(fss.FlagSet("servicemesh"), s.ServiceMeshOptions) - + s.GatewayOptions.AddFlags(fss.FlagSet("gateway"), s.GatewayOptions) fs := fss.FlagSet("leaderelection") s.bindLeaderElectionFlags(s.LeaderElection, fs) diff --git a/cmd/controller-manager/app/server.go b/cmd/controller-manager/app/server.go index 531391877..67468fcf2 100644 --- a/cmd/controller-manager/app/server.go +++ b/cmd/controller-manager/app/server.go @@ -37,6 +37,7 @@ import ( "kubesphere.io/kubesphere/pkg/apis" controllerconfig "kubesphere.io/kubesphere/pkg/apiserver/config" "kubesphere.io/kubesphere/pkg/controller/application" + "kubesphere.io/kubesphere/pkg/controller/helm" "kubesphere.io/kubesphere/pkg/controller/namespace" "kubesphere.io/kubesphere/pkg/controller/network/webhooks" "kubesphere.io/kubesphere/pkg/controller/openpitrix/helmapplication" @@ -76,6 +77,7 @@ func NewControllerManagerCommand() *cobra.Command { NetworkOptions: conf.NetworkOptions, MultiClusterOptions: conf.MultiClusterOptions, ServiceMeshOptions: conf.ServiceMeshOptions, + GatewayOptions: conf.GatewayOptions, LeaderElection: s.LeaderElection, LeaderElect: s.LeaderElect, WebhookCertDir: s.WebhookCertDir, @@ -295,6 +297,14 @@ func run(s *options.KubeSphereControllerManagerOptions, ctx context.Context) err klog.Fatalf("Unable to create ResourceQuota controller: %v", err) } + helmReconciler := helm.Reconciler{} + if !s.GatewayOptions.IsEmpty() { + helmReconciler.WatchFiles = append(helmReconciler.WatchFiles, s.GatewayOptions.WatchesPath) + } + if err := helmReconciler.SetupWithManager(mgr); err != nil { + klog.Fatalf("Unable to create helm controller: %v", err) + } + // TODO(jeff): refactor config with CRD servicemeshEnabled := s.ServiceMeshOptions != nil && len(s.ServiceMeshOptions.IstioPilotHost) != 0 if err = addControllers(mgr, diff --git a/config/crds/gateway.kubesphere.io_gateways.yaml b/config/crds/gateway.kubesphere.io_gateways.yaml new file mode 100644 index 000000000..dae29df2d --- /dev/null +++ b/config/crds/gateway.kubesphere.io_gateways.yaml @@ -0,0 +1,92 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: gateways.gateway.kubesphere.io +spec: + group: gateway.kubesphere.io + names: + kind: Gateway + listKind: GatewayList + plural: gateways + singular: gateway + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Gateway is the Schema for the gateways API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: GatewaySpec defines the desired state of Gateway + properties: + controller: + properties: + annotations: + additionalProperties: + type: string + type: object + replicas: + format: int32 + type: integer + scope: + properties: + enabled: + type: boolean + namespace: + type: string + type: object + type: object + deployment: + properties: + annotations: + additionalProperties: + type: string + type: object + replicas: + format: int32 + type: integer + type: object + service: + properties: + annotations: + additionalProperties: + type: string + type: object + type: + description: Service Type string describes ingress methods for + a service + type: string + type: object + type: object + status: + type: object + x-kubernetes-embedded-resource: true + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/gateway.kubesphere.io_nginxes.yaml b/config/crds/gateway.kubesphere.io_nginxes.yaml new file mode 100644 index 000000000..f2100ec27 --- /dev/null +++ b/config/crds/gateway.kubesphere.io_nginxes.yaml @@ -0,0 +1,44 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: nginxes.gateway.kubesphere.io +spec: + group: gateway.kubesphere.io + names: + kind: Nginx + listKind: NginxList + plural: nginxes + singular: nginx + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Nginx is the Schema for the nginxes API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of Nginx + type: object + x-kubernetes-preserve-unknown-fields: true + status: + description: Status defines the observed state of Nginx + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/gateway/.helmignore b/config/gateway/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/config/gateway/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/config/gateway/Chart.yaml b/config/gateway/Chart.yaml new file mode 100644 index 000000000..469d90832 --- /dev/null +++ b/config/gateway/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +appVersion: 1.16.0 +description: A Helm chart for Kubernetes +name: gateway +type: application +version: 0.1.0 diff --git a/config/gateway/templates/nginx-ingress.yaml b/config/gateway/templates/nginx-ingress.yaml new file mode 100644 index 000000000..2229da6d9 --- /dev/null +++ b/config/gateway/templates/nginx-ingress.yaml @@ -0,0 +1,272 @@ +apiVersion: gateway.kubesphere.io/v1alpha1 +kind: Nginx +metadata: + name: {{ .Release.Name }}-ingress +spec: + fullnameOverride: {{ .Release.Name }} + controller: + # To rolling upgrade from old nginx ingress controller, we have to overide the name pattern + name: "" + image: + {{- with .Values.controller.image }} + {{- toYaml . | nindent 6 }} + {{- end }} + + publishService: + enabled: {{ eq .Values.service.type "LoadBalancer" }} + + # Will add custom configuration options to Nginx https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/ + config: {} + + ## Annotations to be added to the controller config configuration configmap + ## + configAnnotations: {} + + # Will add custom headers before sending traffic to backends according to https://github.com/kubernetes/ingress-nginx/tree/master/docs/examples/customization/custom-headers + proxySetHeaders: {} + + # Will add custom headers before sending response traffic to the client according to: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#add-headers + addHeaders: {} + + # Optionally customize the pod dnsConfig. + dnsConfig: {} + + + # Bare-metal considerations via the host network https://kubernetes.github.io/ingress-nginx/deploy/baremetal/#via-the-host-network + # Ingress status was blank because there is no Service exposing the NGINX Ingress controller in a configuration using the host network, the default --publish-service flag used in standard cloud setups does not apply + reportNodeInternalIp: false + + ## Election ID to use for status update + ## + electionID: ingress-controller-leader-{{ .Release.Name }} + + ## Name of the ingress class to route through this controller + ## + ingressClass: nginx + + # This section refers to the creation of the IngressClass resource + # IngressClass resources are supported since k8s >= 1.18 + ingressClassResource: + enabled: false + default: false + + # Parameters is a link to a custom resource containing additional + # configuration for the controller. This is optional if the controller + # does not require extra parameters. + parameters: {} + + # labels to add to the pod container metadata + podLabels: {} + # key: value + + + ## Limit the scope of the controller + ## +{{- if .Values.controller.scope.enabled }} + scope: + enabled: true + namespace: {{ default .Release.Namespace .Values.controller.scope.namespace }} # defaults to .Release.Namespace +{{- end }} + + + ## Allows customization of the configmap / nginx-configmap namespace + ## + configMapNamespace: "" # defaults to .Release.Namespace + + ## Allows customization of the tcp-services-configmap + ## + tcp: + configMapNamespace: "" # defaults to .Release.Namespace + ## Annotations to be added to the tcp config configmap + annotations: {} + + ## Allows customization of the udp-services-configmap + ## + udp: + configMapNamespace: "" # defaults to .Release.Namespace + ## Annotations to be added to the udp config configmap + annotations: {} + + + ## Additional command line arguments to pass to nginx-ingress-controller + ## E.g. to specify the default SSL certificate you can use + ## extraArgs: + ## default-ssl-certificate: "/" + extraArgs: {} + + ## Additional environment variables to set + extraEnvs: [] + + kind: Deployment + + ## Annotations to be added to the controller Deployment or DaemonSet + ## + {{- if .Values.deployment.annotations }} + annotations: {{ toYaml .Values.deployment.annotations | nindent 6 }} + {{- end }} + + ## Labels to be added to the controller Deployment or DaemonSet + ## + labels: {} + # keel.sh/policy: patch + # keel.sh/trigger: poll + + + ## Node tolerations for server scheduling to nodes with taints + ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + ## + tolerations: [] + # - key: "key" + # operator: "Equal|Exists" + # value: "value" + # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)" + + ## Affinity and anti-affinity + ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## + affinity: {} + # # An example of preferred pod anti-affinity, weight is in the range 1-100 + # podAntiAffinity: + # preferredDuringSchedulingIgnoredDuringExecution: + # - weight: 100 + # podAffinityTerm: + # labelSelector: + # matchExpressions: + # - key: app.kubernetes.io/name + # operator: In + # values: + # - ingress-nginx + # - key: app.kubernetes.io/instance + # operator: In + # values: + # - ingress-nginx + # - key: app.kubernetes.io/component + # operator: In + # values: + # - controller + # topologyKey: kubernetes.io/hostname + + # # An example of required pod anti-affinity + # podAntiAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # - labelSelector: + # matchExpressions: + # - key: app.kubernetes.io/name + # operator: In + # values: + # - ingress-nginx + # - key: app.kubernetes.io/instance + # operator: In + # values: + # - ingress-nginx + # - key: app.kubernetes.io/component + # operator: In + # values: + # - controller + # topologyKey: "kubernetes.io/hostname" + + ## Topology spread constraints rely on node labels to identify the topology domain(s) that each Node is in. + ## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + ## + topologySpreadConstraints: [] + # - maxSkew: 1 + # topologyKey: failure-domain.beta.kubernetes.io/zone + # whenUnsatisfiable: DoNotSchedule + # labelSelector: + # matchLabels: + # app.kubernetes.io/instance: ingress-nginx-internal + + + + replicaCount: {{.Values.deployment.replicas}} + + minAvailable: 1 + + # Define requests resources to avoid probe issues due to CPU utilization in busy nodes + # ref: https://github.com/kubernetes/ingress-nginx/issues/4735#issuecomment-551204903 + # Ideally, there should be no limits. + # https://engineering.indeedblog.com/blog/2019/12/cpu-throttling-regression-fix/ + resources: + # limits: + # cpu: 100m + # memory: 90Mi + requests: + cpu: 100m + memory: 90Mi + + # Mutually exclusive with keda autoscaling + autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 11 + targetCPUUtilizationPercentage: 50 + targetMemoryUtilizationPercentage: 50 + + ## Override NGINX template + customTemplate: + configMapName: "" + configMapKey: "" + + service: + enabled: true + +{{- if .Values.service.annotations }} + annotations: {{ toYaml .Values.service.annotations | nindent 8 }} +{{- end }} + labels: {} + # clusterIP: "" + + ## List of IP addresses at which the controller services are available + ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips + ## + externalIPs: [] + + # loadBalancerIP: "" + loadBalancerSourceRanges: [] + + ## Set external traffic policy to: "Local" to preserve source IP on + ## providers supporting it + ## Ref: https://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-typeloadbalancer + # externalTrafficPolicy: "" + + # Must be either "None" or "ClientIP" if set. Kubernetes will default to "None". + # Ref: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + # sessionAffinity: "" + + + type: {{ .Values.service.type }} + + # type: NodePort + # nodePorts: + # http: 32080 + # https: 32443 + # tcp: + # 8080: 32808 + nodePorts: + http: "" + https: "" + tcp: {} + udp: {} + + admissionWebhooks: + enabled: false + + metrics: + port: 10254 + enabled: true + + serviceMonitor: + enabled: false + prometheusRule: + enabled: false + + + ## Optional array of imagePullSecrets containing private registry credentials + ## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + imagePullSecrets: [] + # - name: secretName + + + + + diff --git a/config/gateway/values.yaml b/config/gateway/values.yaml new file mode 100644 index 000000000..0210eb666 --- /dev/null +++ b/config/gateway/values.yaml @@ -0,0 +1,25 @@ +# Default values for gateway. + +controller: + replicas: 1 + annotations: {} + ## Limit the scope of the controller + ## + scope: + enabled: false + namespace: "" # defaults to .Release.Namespace + image: + repository: kubesphere/ingress-nginx-controller + tag: "v0.48.1" + pullPolicy: IfNotPresent + + +service: + ## annotations for Services, used for config Cloud LoadBalancer + annotations: {} + type: LoadBalancer + +## for nginx controller, same with controller +deployment: + annotations: {} + replicas: 1 \ No newline at end of file diff --git a/pkg/apiserver/config/config.go b/pkg/apiserver/config/config.go index 21bb2e8a4..5e0d0907e 100644 --- a/pkg/apiserver/config/config.go +++ b/pkg/apiserver/config/config.go @@ -32,6 +32,7 @@ import ( "kubesphere.io/kubesphere/pkg/simple/client/cache" "kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins" "kubesphere.io/kubesphere/pkg/simple/client/events" + "kubesphere.io/kubesphere/pkg/simple/client/gateway" "kubesphere.io/kubesphere/pkg/simple/client/k8s" "kubesphere.io/kubesphere/pkg/simple/client/kubeedge" "kubesphere.io/kubesphere/pkg/simple/client/ldap" @@ -104,6 +105,7 @@ type Config struct { NotificationOptions *notification.Options `json:"notification,omitempty" yaml:"notification,omitempty" mapstructure:"notification"` KubeEdgeOptions *kubeedge.Options `json:"kubeedge,omitempty" yaml:"kubeedge,omitempty" mapstructure:"kubeedge"` MeteringOptions *metering.Options `json:"metering,omitempty" yaml:"metering,omitempty" mapstructure:"metering"` + GatewayOptions *gateway.Options `json:"gateway,omitempty" yaml:"router,omitempty" mapstructure:"router"` } // newConfig creates a default non-empty Config @@ -129,6 +131,7 @@ func New() *Config { AuditingOptions: auditing.NewAuditingOptions(), KubeEdgeOptions: kubeedge.NewKubeEdgeOptions(), MeteringOptions: metering.NewMeteringOptions(), + GatewayOptions: gateway.NewGatewayOptions(), } } diff --git a/pkg/apiserver/config/config_test.go b/pkg/apiserver/config/config_test.go index 73bf86cc4..650628783 100644 --- a/pkg/apiserver/config/config_test.go +++ b/pkg/apiserver/config/config_test.go @@ -36,6 +36,7 @@ import ( "kubesphere.io/kubesphere/pkg/simple/client/cache" "kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins" "kubesphere.io/kubesphere/pkg/simple/client/events" + "kubesphere.io/kubesphere/pkg/simple/client/gateway" "kubesphere.io/kubesphere/pkg/simple/client/k8s" "kubesphere.io/kubesphere/pkg/simple/client/kubeedge" "kubesphere.io/kubesphere/pkg/simple/client/ldap" @@ -179,6 +180,10 @@ func newTestConfig() (*Config, error) { MeteringOptions: &metering.Options{ RetentionDay: "7d", }, + GatewayOptions: &gateway.Options{ + WatchesPath: "/etc/kubesphere/watches.yaml", + Namespace: "kubesphere-controls-system", + }, } return conf, nil } diff --git a/pkg/controller/helm/helm_controller.go b/pkg/controller/helm/helm_controller.go new file mode 100644 index 000000000..da881ccec --- /dev/null +++ b/pkg/controller/helm/helm_controller.go @@ -0,0 +1,78 @@ +/* +Copyright 2021 The KubeSphere Authors. + +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 helm + +import ( + "runtime" + "time" + + "k8s.io/klog" + ctrl "sigs.k8s.io/controller-runtime" + + "github.com/operator-framework/helm-operator-plugins/pkg/annotation" + "github.com/operator-framework/helm-operator-plugins/pkg/reconciler" + "github.com/operator-framework/helm-operator-plugins/pkg/watches" +) + +type Reconciler struct { + WatchFiles []string +} + +// SetupWithManager creates reconilers for each helm package that defined in the WatchFiles. +func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { + var watchKinds []watches.Watch + for _, file := range r.WatchFiles { + ws, err := watches.Load(file) + if err != nil { + return err + } + watchKinds = append(watchKinds, ws...) + } + + for _, w := range watchKinds { + // Register controller with the factory + reconcilePeriod := time.Minute + if w.ReconcilePeriod != nil { + reconcilePeriod = w.ReconcilePeriod.Duration + } + + maxConcurrentReconciles := runtime.NumCPU() + if w.MaxConcurrentReconciles != nil { + maxConcurrentReconciles = *w.MaxConcurrentReconciles + } + + r, err := reconciler.New( + reconciler.WithChart(*w.Chart), + reconciler.WithGroupVersionKind(w.GroupVersionKind), + reconciler.WithOverrideValues(w.OverrideValues), + reconciler.SkipDependentWatches(w.WatchDependentResources != nil && !*w.WatchDependentResources), + reconciler.WithMaxConcurrentReconciles(maxConcurrentReconciles), + reconciler.WithReconcilePeriod(reconcilePeriod), + reconciler.WithInstallAnnotations(annotation.DefaultInstallAnnotations...), + reconciler.WithUpgradeAnnotations(annotation.DefaultUpgradeAnnotations...), + reconciler.WithUninstallAnnotations(annotation.DefaultUninstallAnnotations...), + ) + if err != nil { + return err + } + if err := r.SetupWithManager(mgr); err != nil { + return err + } + klog.Info("configured watch", "gvk", w.GroupVersionKind, "chartPath", w.ChartPath, "maxConcurrentReconciles", maxConcurrentReconciles, "reconcilePeriod", reconcilePeriod) + } + return nil +} diff --git a/pkg/controller/helm/helm_controller_test.go b/pkg/controller/helm/helm_controller_test.go new file mode 100644 index 000000000..4b5dc2584 --- /dev/null +++ b/pkg/controller/helm/helm_controller_test.go @@ -0,0 +1,89 @@ +/* +Copyright 2021 KubeSphere Authors + +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 helm + +import ( + "io/ioutil" + "path/filepath" + "testing" + + "k8s.io/client-go/rest" + "k8s.io/klog/klogr" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "sigs.k8s.io/controller-runtime/pkg/envtest" + "sigs.k8s.io/controller-runtime/pkg/envtest/printer" +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var testEnv *envtest.Environment +var k8sClient client.Client + +func TestApplicationController(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecsWithDefaultAndCustomReporters(t, + "Application Controller Test Suite", + []Reporter{printer.NewlineReporter{}}) +} + +var _ = BeforeSuite(func(done Done) { + logf.SetLogger(klogr.New()) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")}, + AttachControlPlaneOutput: false, + } + var err error + cfg, err = testEnv.Start() + Expect(err).ToNot(HaveOccurred()) + Expect(cfg).ToNot(BeNil()) + + close(done) +}, 60) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).ToNot(HaveOccurred()) +}) + +var _ = Context("Helm reconcier", func() { + Describe("Gateway", func() { + It("Should setup gateway helm reconcier", func() { + data := "- group: gateway.kubesphere.io\n version: v1alpha1\n kind: Gateway\n chart: ../../../config/gateway\n" + f, _ := ioutil.TempFile("", "watch") + ioutil.WriteFile(f.Name(), []byte(data), 0) + + mgr, err := ctrl.NewManager(cfg, ctrl.Options{MetricsBindAddress: "0"}) + Expect(err).NotTo(HaveOccurred(), "failed to create a manager") + + reconciler := &Reconciler{} + reconciler.WatchFiles = append(reconciler.WatchFiles, f.Name()) + err = reconciler.SetupWithManager(mgr) + Expect(err).NotTo(HaveOccurred(), "failed to setup helm reconciler") + + }) + }) +}) diff --git a/pkg/simple/client/gateway/options.go b/pkg/simple/client/gateway/options.go new file mode 100644 index 000000000..ddc2e99ec --- /dev/null +++ b/pkg/simple/client/gateway/options.go @@ -0,0 +1,62 @@ +/* +Copyright 2021 KubeSphere Authors + +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 gateway + +import ( + "github.com/spf13/pflag" + + "kubesphere.io/kubesphere/pkg/utils/reflectutils" +) + +// Options contains configuration of the default Gateway +type Options struct { + WatchesPath string `json:"watchesPath,omitempty" yaml:"watchesPath"` + Namespace string `json:"namespace,omitempty" yaml:"namespace"` +} + +// NewGatewayOptions creates a default Gateway Option +func NewGatewayOptions() *Options { + return &Options{ + WatchesPath: "", + Namespace: "", //constants.KubeSphereControlNamespace + } +} + +func (s *Options) IsEmpty() bool { + return s.WatchesPath == "" +} + +// Validate check options values +func (s *Options) Validate() []error { + var errors []error + + return errors +} + +// ApplyTo overrides options if it's valid, which watchesPath is not empty +func (s *Options) ApplyTo(options *Options) { + if s.WatchesPath != "" { + reflectutils.Override(options, s) + } +} + +// AddFlags add options flags to command line flags, +// if watchesPath left empty, following options will be ignored +func (s *Options) AddFlags(fs *pflag.FlagSet, c *Options) { + fs.StringVar(&s.WatchesPath, "watches-path", c.WatchesPath, "Path to the watches file to use.") + fs.StringVar(&s.Namespace, "namespace", c.Namespace, "Working Namespace of the Gateway's Ingress Controller.") +}