Skip to content

Commit

Permalink
feat: add COSI controller handler
Browse files Browse the repository at this point in the history
  • Loading branch information
dkoshkin committed Jan 7, 2025
1 parent d3490ae commit 9826e25
Show file tree
Hide file tree
Showing 17 changed files with 390 additions and 1 deletion.
3 changes: 2 additions & 1 deletion api/v1alpha1/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ const (
CNIVariableName = "cni"
// NFDVariableName is the NFD external patch variable name.
NFDVariableName = "nfd"

// COSIVariableName is the COSI external patch variable name.
COSIVariableName = "cosi"
// ClusterAutoscalerVariableName is the cluster-autoscaler external patch variable name.
ClusterAutoscalerVariableName = "clusterAutoscaler"
// ServiceLoadBalancerVariableName is the Service LoadBalancer config patch variable name.
Expand Down
2 changes: 2 additions & 0 deletions charts/cluster-api-runtime-extensions-nutanix/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ A Helm chart for cluster-api-runtime-extensions-nutanix
| hooks.cni.cilium.crsStrategy.defaultCiliumConfigMap.name | string | `"cilium"` | |
| hooks.cni.cilium.helmAddonStrategy.defaultValueTemplateConfigMap.create | bool | `true` | |
| hooks.cni.cilium.helmAddonStrategy.defaultValueTemplateConfigMap.name | string | `"default-cilium-cni-helm-values-template"` | |
| hooks.cosi.controller.helmAddonStrategy.defaultValueTemplateConfigMap.create | bool | `true` | |
| hooks.cosi.controller.helmAddonStrategy.defaultValueTemplateConfigMap.name | string | `"default-cosi-controller-helm-values-template"` | |
| hooks.csi.aws-ebs.helmAddonStrategy.defaultValueTemplateConfigMap.create | bool | `true` | |
| hooks.csi.aws-ebs.helmAddonStrategy.defaultValueTemplateConfigMap.name | string | `"default-aws-ebs-csi-helm-values-template"` | |
| hooks.csi.local-path.helmAddonStrategy.defaultValueTemplateConfigMap.create | bool | `true` | |
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright 2024 Nutanix. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

{{- if (index .Values.hooks.cosi "controller").helmAddonStrategy.defaultValueTemplateConfigMap.create }}
apiVersion: v1
kind: ConfigMap
metadata:
name: '{{ (index .Values.hooks.cosi "controller").helmAddonStrategy.defaultValueTemplateConfigMap.name }}'
data:
values.yaml: |-
{{- .Files.Get "addons/cosi/controller/values-template.yaml" | nindent 4 }}
{{- end -}}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ spec:
- --csi.local-path.helm-addon.default-values-template-configmap-name={{ (index .Values.hooks.csi "local-path").helmAddonStrategy.defaultValueTemplateConfigMap.name }}
- --csi.snapshot-controller.helm-addon.default-values-template-configmap-name={{ (index .Values.hooks.csi "snapshot-controller").helmAddonStrategy.defaultValueTemplateConfigMap.name }}
- --ccm.aws.helm-addon.default-values-template-configmap-name={{ .Values.hooks.ccm.aws.helmAddonStrategy.defaultValueTemplateConfigMap.name }}
- --cosi.controller.helm-addon.default-values-template-configmap-name={{ .Values.hooks.cosi.controller.helmAddonStrategy.defaultValueTemplateConfigMap.name }}
{{- range $k, $v := .Values.hooks.ccm.aws.k8sMinorVersionToCCMVersion }}
- --ccm.aws.aws-ccm-versions={{ $k }}={{ $v }}
{{- end }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ data:
ChartName: cluster-autoscaler
ChartVersion: 9.43.2
RepositoryURL: '{{ if .Values.helmRepository.enabled }}oci://helm-repository.{{ .Release.Namespace }}.svc/charts{{ else }}https://kubernetes.github.io/autoscaler{{ end }}'
cosi-controller: |
ChartName: cosi
ChartVersion: 0.0.1-alpha.1
RepositoryURL: '{{ if .Values.helmRepository.enabled }}oci://helm-repository.{{ .Release.Namespace }}.svc/charts{{ else }}https://mesosphere.github.io/charts/stable/{{ end }}'
local-path-provisioner-csi: |
ChartName: local-path-provisioner
ChartVersion: 0.0.30
Expand Down
26 changes: 26 additions & 0 deletions charts/cluster-api-runtime-extensions-nutanix/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,32 @@
},
"type": "object"
},
"cosi": {
"properties": {
"controller": {
"properties": {
"helmAddonStrategy": {
"properties": {
"defaultValueTemplateConfigMap": {
"properties": {
"create": {
"type": "boolean"
},
"name": {
"type": "string"
}
},
"type": "object"
}
},
"type": "object"
}
},
"type": "object"
}
},
"type": "object"
},
"csi": {
"properties": {
"aws-ebs": {
Expand Down
6 changes: 6 additions & 0 deletions charts/cluster-api-runtime-extensions-nutanix/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ hooks:
defaultTemplateConfigMap:
create: true
name: default-kube-vip-template
cosi:
controller:
helmAddonStrategy:
defaultValueTemplateConfigMap:
create: true
name: default-cosi-controller-helm-values-template

helmAddonsConfigMap: default-helm-addons-config

Expand Down
32 changes: 32 additions & 0 deletions docs/content/addons/cosi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
+++
title = " Container Object Storage Interface (COSI)"
icon = "fa-solid fa-eye"
+++

By leveraging CAPI cluster lifecycle hooks, this handler deploys [Container Object Storage Interface] (COSI)
on the new cluster at the `AfterControlPlaneInitialized` phase.

Deployment of COSI is opt-in via the [provider-specific cluster configuration]({{< ref ".." >}}).

The hook uses the [Cluster API Add-on Provider for Helm] to deploy the NFD resources.

## Example

To enable deployment of COSI on a cluster, specify the following values:

```yaml
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
name: <NAME>
spec:
topology:
variables:
- name: clusterConfig
value:
addons:
cosi: {}
```
[Container Object Storage Interface]: https://kubernetes.io/blog/2022/09/02/cosi-kubernetes-object-storage-management/
[Cluster API Add-on Provider for Helm]: https://github.com/kubernetes-sigs/cluster-api-addon-provider-helm
5 changes: 5 additions & 0 deletions hack/addons/helm-chart-bundler/repos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ repositories:
charts:
cluster-autoscaler:
- 9.43.2
cosi:
repoURL: https://mesosphere.github.io/charts/stable/
charts:
cosi:
- 0.0.1-alpha.1
local-path-provisioner:
repoURL: https://charts.containeroo.ch
charts:
Expand Down
17 changes: 17 additions & 0 deletions hack/addons/kustomize/cosi-controller/kustomization.yaml.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2024 Nutanix. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

metadata:
name: cosi-controller-kustomize

helmCharts:
- name: cosi
namespace: container-object-storage-system
repo: https://mesosphere.github.io/charts/stable/
releaseName: cosi-controller
version: ${COSI_CONTROLLER_VERSION}
includeCRDs: true
skipTests: true
2 changes: 2 additions & 0 deletions make/addons.mk
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export KUBE_VIP_VERSION := v0.8.3

export METALLB_CHART_VERSION := 0.14.8

export COSI_CONTROLLER_VERSION := 0.0.1-alpha.1

.PHONY: addons.sync
addons.sync: $(addprefix update-addon.,calico cilium nfd cluster-autoscaler snapshot-controller local-path-provisioner-csi aws-ebs-csi kube-vip)
addons.sync: $(addprefix update-addon.aws-ccm.,127 128 129 130 131)
Expand Down
1 change: 1 addition & 0 deletions pkg/handlers/generic/lifecycle/config/cm.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const (
LocalPathProvisionerCSI Component = "local-path-provisioner-csi"
AWSEBSCSI Component = "aws-ebs-csi"
AWSCCM Component = "aws-ccm"
COSIController Component = "cosi-controller"
)

type HelmChartGetter struct {
Expand Down
8 changes: 8 additions & 0 deletions pkg/handlers/generic/lifecycle/cosi/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright 2023 Nutanix. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

// Package COSI provides a handler for managing NFD deployments on clusters
//
// +kubebuilder:rbac:groups=addons.cluster.x-k8s.io,resources=clusterresourcesets,verbs=watch;list;get;create;patch;update;delete
// +kubebuilder:rbac:groups="",resources=configmaps,verbs=watch;list;get;create;patch;update;delete
package cosi
192 changes: 192 additions & 0 deletions pkg/handlers/generic/lifecycle/cosi/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// Copyright 2023 Nutanix. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package cosi

import (
"context"
"fmt"

"github.com/spf13/pflag"
"k8s.io/utils/ptr"
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
ctrl "sigs.k8s.io/controller-runtime"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"

"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
apivariables "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/variables"
commonhandlers "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers"
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/lifecycle"
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables"
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/addons"
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/config"
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/options"
)

const (
defaultHelmReleaseName = "cosi-controller"
defaultHelmReleaseNamespace = "container-object-storage-system"
)

type ControllerConfig struct {
*options.GlobalOptions

helmAddonConfig *addons.HelmAddonConfig
}

func NewControllerConfig(globalOptions *options.GlobalOptions) *ControllerConfig {
return &ControllerConfig{
GlobalOptions: globalOptions,
helmAddonConfig: addons.NewHelmAddonConfig(
"default-cosi-controller-helm-values-template",
defaultHelmReleaseNamespace,
defaultHelmReleaseName,
),
}
}

func (c *ControllerConfig) AddFlags(prefix string, flags *pflag.FlagSet) {
c.helmAddonConfig.AddFlags(prefix+".helm-addon", flags)
}

type DefaultCOSIController struct {
client ctrlclient.Client
config *ControllerConfig
helmChartInfoGetter *config.HelmChartGetter

variableName string // points to the global config variable
variablePath []string // path of this variable on the global config variable
}

var (
_ commonhandlers.Named = &DefaultCOSIController{}
_ lifecycle.AfterControlPlaneInitialized = &DefaultCOSIController{}
_ lifecycle.BeforeClusterUpgrade = &DefaultCOSIController{}
)

func New(
c ctrlclient.Client,
cfg *ControllerConfig,
helmChartInfoGetter *config.HelmChartGetter,
) *DefaultCOSIController {
return &DefaultCOSIController{
client: c,
config: cfg,
helmChartInfoGetter: helmChartInfoGetter,
variableName: v1alpha1.ClusterConfigVariableName,
variablePath: []string{"addons", v1alpha1.COSIVariableName},
}
}

func (n *DefaultCOSIController) Name() string {
return "COSIControllerHandler"
}

func (n *DefaultCOSIController) AfterControlPlaneInitialized(
ctx context.Context,
req *runtimehooksv1.AfterControlPlaneInitializedRequest,
resp *runtimehooksv1.AfterControlPlaneInitializedResponse,
) {
commonResponse := &runtimehooksv1.CommonResponse{}
n.apply(ctx, &req.Cluster, commonResponse)
resp.Status = commonResponse.GetStatus()
resp.Message = commonResponse.GetMessage()
}

func (n *DefaultCOSIController) BeforeClusterUpgrade(
ctx context.Context,
req *runtimehooksv1.BeforeClusterUpgradeRequest,
resp *runtimehooksv1.BeforeClusterUpgradeResponse,
) {
commonResponse := &runtimehooksv1.CommonResponse{}
n.apply(ctx, &req.Cluster, commonResponse)
resp.Status = commonResponse.GetStatus()
resp.Message = commonResponse.GetMessage()
}

func (n *DefaultCOSIController) apply(
ctx context.Context,
cluster *clusterv1.Cluster,
resp *runtimehooksv1.CommonResponse,
) {
clusterKey := ctrlclient.ObjectKeyFromObject(cluster)

log := ctrl.LoggerFrom(ctx).WithValues(
"cluster",
clusterKey,
)

varMap := variables.ClusterVariablesToVariablesMap(cluster.Spec.Topology.Variables)

cosiVar, err := variables.Get[apivariables.COSI](varMap, n.variableName, n.variablePath...)
if err != nil {
if variables.IsNotFoundError(err) {
log.
Info(
"Skipping COSI handler, cluster does not specify request COSI Controller addon deployment",
)
return
}
log.Error(
err,
"failed to read COSI variable from cluster definition",
)
resp.SetStatus(runtimehooksv1.ResponseStatusFailure)
resp.SetMessage(
fmt.Sprintf("failed to read COSI variable from cluster definition: %v",
err,
),
)
return
}

var strategy addons.Applier
switch ptr.Deref(cosiVar.Strategy, "") {
case v1alpha1.AddonStrategyHelmAddon:
helmChart, err := n.helmChartInfoGetter.For(ctx, log, config.COSIController)
if err != nil {
log.Error(
err,
"failed to get configmap with helm settings",
)
resp.SetStatus(runtimehooksv1.ResponseStatusFailure)
resp.SetMessage(
fmt.Sprintf("failed to get configuration to create helm addon: %v",
err,
),
)
return
}
strategy = addons.NewHelmAddonApplier(
n.config.helmAddonConfig,
n.client,
helmChart,
)
case v1alpha1.AddonStrategyClusterResourceSet:
resp.SetStatus(runtimehooksv1.ResponseStatusFailure)
resp.SetMessage(
fmt.Sprintf(
"strategy %q not provided for COSI", v1alpha1.AddonStrategyClusterResourceSet,
),
)
return
case "":
resp.SetStatus(runtimehooksv1.ResponseStatusFailure)
resp.SetMessage("strategy not provided for COSI")
return
default:
resp.SetStatus(runtimehooksv1.ResponseStatusFailure)
resp.SetMessage(fmt.Sprintf("unknown COSI addon deployment strategy %q", *cosiVar.Strategy))
return
}

if err := strategy.Apply(ctx, cluster, n.config.DefaultsNamespace(), log); err != nil {
err = fmt.Errorf("failed to apply COSI addon: %w", err)
resp.SetStatus(runtimehooksv1.ResponseStatusFailure)
resp.SetMessage(err.Error())
return
}

resp.SetStatus(runtimehooksv1.ResponseStatusSuccess)
}
Loading

0 comments on commit 9826e25

Please sign in to comment.