diff --git a/api/v1alpha1/clusterconfig_types.go b/api/v1alpha1/clusterconfig_types.go index 5cee9a2fb..f19b35ea8 100644 --- a/api/v1alpha1/clusterconfig_types.go +++ b/api/v1alpha1/clusterconfig_types.go @@ -12,6 +12,7 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/openapi/patterns" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/variables" ) type StorageProvisioner string @@ -94,6 +95,14 @@ func (s ClusterConfigSpec) VariableSchema() clusterv1.VariableSchema { //nolint: return clusterConfigProps } +func (s *ClusterConfigSpec) ToClusterVariable(name string) (*clusterv1.ClusterVariable, error) { + return variables.MarshalToClusterVariable(name, s) +} + +func (s *ClusterConfigSpec) FromClusterVariable(clusterVariable *clusterv1.ClusterVariable) error { + return variables.UnmarshalClusterVariable(clusterVariable, s) +} + // GenericClusterConfig defines the generic cluster configdesired. type GenericClusterConfig struct { // +optional diff --git a/api/v1alpha1/node_types.go b/api/v1alpha1/node_types.go index f2d3c6bf3..f1046445e 100644 --- a/api/v1alpha1/node_types.go +++ b/api/v1alpha1/node_types.go @@ -8,16 +8,18 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/variables" ) -//+kubebuilder:object:root=true +// +kubebuilder:object:root=true // NodeConfig is the Schema for the workerconfigs API. type NodeConfig struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - //+optional + // +optional Spec NodeConfigSpec `json:"spec,omitempty"` } @@ -63,6 +65,14 @@ func (s NodeConfigSpec) VariableSchema() clusterv1.VariableSchema { return nodeConfigProps } +func (s *NodeConfigSpec) ToClusterVariable(name string) (*clusterv1.ClusterVariable, error) { + return variables.MarshalToClusterVariable(name, s) +} + +func (s *NodeConfigSpec) FromClusterVariable(clusterVariable *clusterv1.ClusterVariable) error { + return variables.UnmarshalClusterVariable(clusterVariable, s) +} + type GenericNodeConfig struct{} func (GenericNodeConfig) VariableSchema() clusterv1.VariableSchema { diff --git a/api/variables/variables.go b/api/variables/variables.go new file mode 100644 index 000000000..a570410f5 --- /dev/null +++ b/api/variables/variables.go @@ -0,0 +1,59 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package variables + +import ( + "encoding/json" + "fmt" + + v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" +) + +func MarshalToClusterVariable[T any](name string, obj T) (*clusterv1.ClusterVariable, error) { + marshaled, err := json.Marshal(obj) + if err != nil { + return nil, fmt.Errorf("failed to marshal variable value %q: %w", name, err) + } + return &clusterv1.ClusterVariable{ + Name: name, + Value: v1.JSON{Raw: marshaled}, + }, nil +} + +func UnmarshalClusterVariable[T any](clusterVariable *clusterv1.ClusterVariable, obj *T) error { + err := json.Unmarshal(clusterVariable.Value.Raw, obj) + if err != nil { + return fmt.Errorf("error unmarshalling variable: %w", err) + } + + return nil +} + +func GetClusterVariableByName( + name string, + clusterVariables []clusterv1.ClusterVariable, +) (*clusterv1.ClusterVariable, int) { + for i, clusterVar := range clusterVariables { + if clusterVar.Name == name { + return &clusterVar, i + } + } + return nil, -1 +} + +func GetMachineDeploymentVariableByName( + name string, + machineDeploymentVariables *clusterv1.MachineDeploymentVariables, +) (*clusterv1.ClusterVariable, int) { + if machineDeploymentVariables == nil { + return nil, -1 + } + for i, mdVar := range machineDeploymentVariables.Overrides { + if mdVar.Name == name { + return &mdVar, i + } + } + return nil, -1 +} diff --git a/charts/cluster-api-runtime-extensions-nutanix/README.md b/charts/cluster-api-runtime-extensions-nutanix/README.md index 80c72c84b..5bbea1aec 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/README.md +++ b/charts/cluster-api-runtime-extensions-nutanix/README.md @@ -77,3 +77,6 @@ A Helm chart for cluster-api-runtime-extensions-nutanix | service.port | int | `443` | | | service.type | string | `"ClusterIP"` | | | tolerations | list | `[{"effect":"NoSchedule","key":"node-role.kubernetes.io/master","operator":"Equal"},{"effect":"NoSchedule","key":"node-role.kubernetes.io/control-plane","operator":"Equal"}]` | Kubernetes pod tolerations | +| webhooks.service.annotations | object | `{}` | | +| webhooks.service.port | int | `443` | | +| webhooks.service.type | string | `"ClusterIP"` | | diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/certificates.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/certificates.yaml index e07fcc3dc..aa8891313 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/templates/certificates.yaml +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/certificates.yaml @@ -4,7 +4,7 @@ apiVersion: cert-manager.io/v1 kind: Certificate metadata: - name: {{ template "chart.name" . }}-runtimehooks-tls + name: {{ template "chart.name" . }}-service-cert namespace: {{ .Release.Namespace }} labels: {{- include "chart.labels" . | nindent 4 }} @@ -12,7 +12,9 @@ spec: dnsNames: - {{ template "chart.name" . }}-runtimehooks.{{ .Release.Namespace }}.svc - {{ template "chart.name" . }}-runtimehooks.{{ .Release.Namespace }}.svc.cluster.local + - {{ template "chart.name" . }}-webhook-service.{{ .Release.Namespace }}.svc + - {{ template "chart.name" . }}-webhook-service.{{ .Release.Namespace }}.svc.cluster.local issuerRef: kind: {{ .Values.certificates.issuer.kind }} name: {{ template "chart.issuerName" . }} - secretName: {{ template "chart.name" . }}-runtimehooks-tls + secretName: {{ template "chart.name" . }}-service-cert diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/deployment.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/deployment.yaml index b54b1d72d..1b86dc255 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/templates/deployment.yaml +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/deployment.yaml @@ -28,11 +28,14 @@ spec: image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default $.Chart.AppVersion }}" imagePullPolicy: "{{ .Values.image.pullPolicy }}" args: - - --webhook-cert-dir=/runtimehooks-certs/ + - --runtime-webhook-port=9444 + - --runtime-webhook-cert-dir=/tmp/k8s-webhook-server/serving-certs/ - --defaults-namespace=$(POD_NAMESPACE) - --helm-addons-configmap={{ .Values.helmAddonsConfigMap }} - --cni.cilium.helm-addon.default-values-template-configmap-name={{ .Values.hooks.cni.cilium.helmAddonStrategy.defaultValueTemplateConfigMap.name }} - --nfd.helm-addon.default-values-template-configmap-name={{ .Values.hooks.nfd.helmAddonStrategy.defaultValueTemplateConfigMap.name }} + - --webhook-port=9443 + - --webhook-cert-dir=/tmp/k8s-webhook-server/serving-certs/ {{- range $key, $value := .Values.extraArgs }} - --{{ $key }}={{ $value }} {{- end }} @@ -43,9 +46,12 @@ spec: {{- end }} {{- end }} ports: - - containerPort: 9443 + - containerPort: 9444 name: runtimehooks protocol: TCP + - containerPort: 9443 + name: webhook-server + protocol: TCP - containerPort: 8080 name: metrics protocol: TCP @@ -62,8 +68,8 @@ spec: {{- toYaml . | nindent 10 }} {{- end }} volumeMounts: - - mountPath: /runtimehooks-certs - name: runtimehooks-cert + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert readOnly: true livenessProbe: httpGet: @@ -81,7 +87,7 @@ spec: {{- toYaml . | nindent 8}} {{- end }} volumes: - - name: runtimehooks-cert + - name: cert secret: defaultMode: 420 - secretName: {{ template "chart.name" . }}-runtimehooks-tls + secretName: {{ template "chart.name" . }}-service-cert diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/extensionconfig.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/extensionconfig.yaml index 4f6005b48..535568a19 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/templates/extensionconfig.yaml +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/extensionconfig.yaml @@ -5,7 +5,7 @@ apiVersion: runtime.cluster.x-k8s.io/v1alpha1 kind: ExtensionConfig metadata: annotations: - runtime.cluster.x-k8s.io/inject-ca-from-secret: {{ .Release.Namespace }}/{{ template "chart.name" . }}-runtimehooks-tls + runtime.cluster.x-k8s.io/inject-ca-from-secret: {{ .Release.Namespace }}/{{ template "chart.name" . }}-service-cert name: {{ template "chart.name" . }} namespace: {{ .Release.Namespace }} spec: diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/webhook-service.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/webhook-service.yaml new file mode 100644 index 000000000..056648bc9 --- /dev/null +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/webhook-service.yaml @@ -0,0 +1,26 @@ +# Copyright 2023 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: v1 +kind: Service +metadata: + annotations: + {{- with .Values.webhooks.service.annotations }} + {{ toYaml . | nindent 4 }} + {{- end }} + labels: + {{- include "chart.labels" . | nindent 4 }} + name: {{ template "chart.name" . }}-webhook-service + namespace: {{ .Release.Namespace }} +spec: + type: {{.Values.webhooks.service.type}} + ports: + - name: https + port: {{ .Values.webhooks.service.port }} + protocol: TCP + targetPort: webhook-server + {{- if and .Values.webhooks.service.nodePort (eq "NodePort" .Values.service.type) }} + nodePort: {{ .Values.webhooks.service.nodePort }} + {{- end }} + selector: + {{- include "chart.selectorLabels" . | nindent 4 }} diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/webhook.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/webhook.yaml new file mode 100644 index 000000000..3454aa81e --- /dev/null +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/webhook.yaml @@ -0,0 +1,32 @@ +# Copyright 2023 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: {{ template "chart.name" . }}-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ template "chart.name" . }}-service-cert +webhooks: +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: {{ template "chart.name" . }}-webhook-service + namespace: {{ .Release.Namespace }} + path: /mutate-cluster-x-k8s-io-v1beta1-cluster + failurePolicy: Fail + matchPolicy: Equivalent + name: default.cluster.cluster.x-k8s.io + rules: + - apiGroups: + - cluster.x-k8s.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - clusters + sideEffects: None diff --git a/charts/cluster-api-runtime-extensions-nutanix/values.yaml b/charts/cluster-api-runtime-extensions-nutanix/values.yaml index eee0993af..519ea518a 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/values.yaml +++ b/charts/cluster-api-runtime-extensions-nutanix/values.yaml @@ -128,3 +128,9 @@ tolerations: # -- Priority class to be used for the pod. priorityClassName: system-cluster-critical + +webhooks: + service: + annotations: {} + type: ClusterIP + port: 443 diff --git a/cmd/main.go b/cmd/main.go index f43f3ff7e..39887e02e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -23,6 +23,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/manager" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" caaphv1 "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/external/sigs.k8s.io/cluster-api-addon-provider-helm/api/v1alpha1" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers" @@ -38,6 +39,7 @@ import ( nutanixmutation "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/mutation" nutanixworkerconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/workerconfig" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/options" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/webhooks" ) func main() { @@ -80,6 +82,9 @@ func main() { runtimeWebhookServerOpts := server.NewServerOptions() + // mutating/validating webhook server options + webhookServerOpts := webhooks.NewOptions() + globalOptions := options.NewGlobalOptions() genericLifecycleHandlers := lifecycle.New(globalOptions) @@ -89,6 +94,7 @@ func main() { logsv1.AddFlags(logOptions, pflag.CommandLine) globalOptions.AddFlags(pflag.CommandLine) runtimeWebhookServerOpts.AddFlags(pflag.CommandLine) + webhookServerOpts.AddFlags(pflag.CommandLine) genericLifecycleHandlers.AddFlags(pflag.CommandLine) pflag.CommandLine.SetNormalizeFunc(cliflag.WordSepNormalizeFunc) pflag.CommandLine.AddGoFlagSet(flag.CommandLine) @@ -105,6 +111,14 @@ func main() { // Add the klog logger in the context. ctrl.SetLogger(klog.Background()) + // Setup mutating/validating webhook server + mgrOptions.WebhookServer = webhook.NewServer( + webhook.Options{ + Port: webhookServerOpts.WebhookPort, + CertDir: webhookServerOpts.WebhookCertDir, + }, + ) + signalCtx := ctrl.SetupSignalHandler() mgr, err := newManager(mgrOptions) @@ -154,6 +168,11 @@ func main() { os.Exit(1) } + if err := (&webhooks.Cluster{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "Cluster") + os.Exit(1) + } + if err := mgr.Start(signalCtx); err != nil { setupLog.Error(err, "unable to start controller manager") os.Exit(1) diff --git a/common/pkg/server/server.go b/common/pkg/server/server.go index cdafc567d..0c56d95ed 100644 --- a/common/pkg/server/server.go +++ b/common/pkg/server/server.go @@ -48,13 +48,13 @@ func NewServerOptions() *ServerOptions { } func (s *ServerOptions) AddFlags(fs *pflag.FlagSet) { - fs.IntVar(&s.webhookPort, "webhook-port", s.webhookPort, "Webhook Server port") + fs.IntVar(&s.webhookPort, "runtime-webhook-port", s.webhookPort, "Runtime webhook server port") fs.StringVar( &s.webhookCertDir, - "webhook-cert-dir", + "runtime-webhook-cert-dir", s.webhookCertDir, - "Runtime hooks server cert dir.", + "Runtime webhook server cert dir", ) } diff --git a/make/go.mk b/make/go.mk index 596b2e51c..d405e33f0 100644 --- a/make/go.mk +++ b/make/go.mk @@ -190,7 +190,10 @@ go-fix.%: ; $(info $(M) go fixing $* module) go-generate: ## Runs go generate go-generate: ; $(info $(M) running go generate) go generate -x ./... - controller-gen paths="./..." rbac:headerFile="hack/license-header.yaml.txt",roleName=cluster-api-runtime-extensions-nutanix-manager-role output:rbac:artifacts:config=charts/cluster-api-runtime-extensions-nutanix/templates + controller-gen \ + paths="./..." \ + rbac:headerFile="hack/license-header.yaml.txt",roleName=cluster-api-runtime-extensions-nutanix-manager-role \ + output:rbac:artifacts:config=charts/cluster-api-runtime-extensions-nutanix/templates sed --in-place 's/cluster-api-runtime-extensions-nutanix-manager-role/{{ include "chart.name" . }}-manager-role/' charts/cluster-api-runtime-extensions-nutanix/templates/role.yaml controller-gen paths="./api/v1alpha1/..." object:headerFile="hack/license-header.go.txt" output:object:artifacts:config=/dev/null $(MAKE) go-fix diff --git a/webhooks/cluster.go b/webhooks/cluster.go new file mode 100644 index 000000000..0bdea09a7 --- /dev/null +++ b/webhooks/cluster.go @@ -0,0 +1,142 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package webhooks + +import ( + "context" + "fmt" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation/field" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/variables" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/workerconfig" +) + +// +kubebuilder:webhook:verbs=create;update,path=/mutate-cluster-x-k8s-io-v1beta1-cluster,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=cluster.x-k8s.io,resources=clusters,versions=v1beta1,name=default.cluster.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1 + +// Cluster implements a defaulting webhook for Cluster. +type Cluster struct{} + +var _ webhook.CustomDefaulter = &Cluster{} + +// SetupWebhookWithManager sets up Cluster webhooks. +func (webhook *Cluster) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(&clusterv1.Cluster{}). + WithDefaulter(webhook). + Complete() +} + +// Default satisfies the defaulting webhook interface. +func (webhook *Cluster) Default(_ context.Context, obj runtime.Object) error { + // We gather all defaulting errors and return them together. + var allErrs field.ErrorList + + cluster, ok := obj.(*clusterv1.Cluster) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", obj)) + } + + if cluster.Spec.Topology == nil || + len(cluster.Spec.Topology.Variables) == 0 { + return nil + } + + // Set defaults for 'clusterConfig' variable from spec.topology.variables + clusterConfigVariable, clusterConfigVariableIndex := variables.GetClusterVariableByName( + clusterconfig.MetaVariableName, + cluster.Spec.Topology.Variables, + ) + if clusterConfigVariable != nil { + clusterConfigSpec := &v1alpha1.ClusterConfigSpec{} + err := clusterConfigSpec.FromClusterVariable(clusterConfigVariable) + if err != nil { + return fmt.Errorf("failed to unmarshal ClusterConfigSpec from ClusterVariable: %w", err) + } + errs := defaultClusterConfig(clusterConfigSpec) + if len(errs) > 1 { + allErrs = append(allErrs, errs...) + } + clusterConfigVariable, err = clusterConfigSpec.ToClusterVariable(clusterconfig.MetaVariableName) + if err != nil { + return fmt.Errorf("failed to marshal ClusterConfigSpec to ClusterVariable: %w", err) + } + cluster.Spec.Topology.Variables[clusterConfigVariableIndex] = *clusterConfigVariable + + } + + // Set defaults from 'workerConfig' variable from spec.topology.variables + workerConfigVariable, workerConfigVariableIndex := variables.GetClusterVariableByName( + workerconfig.MetaVariableName, + cluster.Spec.Topology.Variables, + ) + if workerConfigVariable != nil { + workerConfigSpec := &v1alpha1.NodeConfigSpec{} + err := workerConfigSpec.FromClusterVariable(workerConfigVariable) + if err != nil { + return fmt.Errorf("failed to unmarshal NodeConfigSpec from WorkerVariable: %w", err) + } + errs := defaultWorkerConfig(workerConfigSpec) + if len(errs) > 1 { + allErrs = append(allErrs, errs...) + } + workerConfigVariable, err = workerConfigSpec.ToClusterVariable(workerconfig.MetaVariableName) + if err != nil { + return fmt.Errorf("failed to marshal NodeConfigSpec to WorkerVariable: %w", err) + } + cluster.Spec.Topology.Variables[workerConfigVariableIndex] = *workerConfigVariable + } + + // Set defaults for 'workerConfig' variable from spec.topology.workers.machineDeployments.variables.overrides + if cluster.Spec.Topology.Workers != nil { + for i, md := range cluster.Spec.Topology.Workers.MachineDeployments { + mdWorkerConfigVariable, mdWorkerConfigVariableIndex := variables.GetMachineDeploymentVariableByName( + workerconfig.MetaVariableName, + md.Variables, + ) + if mdWorkerConfigVariable != nil { + workerConfigSpec := &v1alpha1.NodeConfigSpec{} + err := workerConfigSpec.FromClusterVariable(mdWorkerConfigVariable) + if err != nil { + return fmt.Errorf("failed to unmarshal NodeConfigSpec from WorkerVariable: %w", err) + } + errs := defaultWorkerConfig(workerConfigSpec) + if len(errs) > 1 { + allErrs = append(allErrs, errs...) + } + mdWorkerConfigVariable, err = workerConfigSpec.ToClusterVariable(workerconfig.MetaVariableName) + if err != nil { + return fmt.Errorf("failed to marshal NodeConfigSpec to WorkerVariable: %w", err) + } + cluster.Spec.Topology.Workers.MachineDeployments[i].Variables.Overrides[mdWorkerConfigVariableIndex] = + *mdWorkerConfigVariable + } + } + } + + if len(allErrs) > 0 { + return apierrors.NewInvalid(clusterv1.GroupVersion.WithKind("Cluster").GroupKind(), cluster.Name, allErrs) + } + + return nil +} + +func defaultClusterConfig(clusterConfig *v1alpha1.ClusterConfigSpec) field.ErrorList { + // Set defaults for clusterConfig variable here. + + return nil +} + +func defaultWorkerConfig(workerConfig *v1alpha1.NodeConfigSpec) field.ErrorList { + // Set defaults for workerConfig variable here. + + return nil +} diff --git a/webhooks/webhooks.go b/webhooks/webhooks.go new file mode 100644 index 000000000..f1390fa51 --- /dev/null +++ b/webhooks/webhooks.go @@ -0,0 +1,26 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package webhooks + +import "github.com/spf13/pflag" + +type Options struct { + WebhookPort int + WebhookCertDir string +} + +func NewOptions() *Options { + return &Options{} +} + +func (s *Options) AddFlags(fs *pflag.FlagSet) { + fs.IntVar(&s.WebhookPort, "webhook-port", s.WebhookPort, "Webhook server port") + + fs.StringVar( + &s.WebhookCertDir, + "webhook-cert-dir", + s.WebhookCertDir, + "Webhook server cert dir", + ) +}