From 1df21838f5de13cc05422494ad564268d78818bb Mon Sep 17 00:00:00 2001 From: Nick Saika Date: Wed, 5 Jun 2024 18:43:43 +0000 Subject: [PATCH] Use environment variables for configuration Within the Helm chart templates, the containers running "linode-blockstorage-csi-driver" were being configured with command-line flags where the flags' values were coming from environment variables. This commit removes the middle-man, and drops all of the CSI driver-specific command-line flags in favour of reading configuration values directly from the environment. The Helm chart templates have been updated accordingly. --- README.md | 246 +++++++++++++----- .../kubernetes/base/ds-csi-linode-node.yaml | 6 +- .../base/ss-csi-linode-controller.yaml | 7 +- go.mod | 1 + go.sum | 2 + .../templates/csi-linode-controller.yaml | 9 +- .../csi-driver/templates/daemonset.yaml | 5 +- helm-chart/csi-driver/values.yaml | 4 + main.go | 72 +++-- 9 files changed, 235 insertions(+), 117 deletions(-) diff --git a/README.md b/README.md index 0be9dd1c..502c8868 100644 --- a/README.md +++ b/README.md @@ -2,22 +2,33 @@ [![Test](https://github.com/linode/linode-blockstorage-csi-driver/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/linode/linode-blockstorage-csi-driver/actions/workflows/test.yml) [![Docker build](https://github.com/linode/linode-blockstorage-csi-driver/actions/workflows/docker-hub.yml/badge.svg?branch=master)](https://github.com/linode/linode-blockstorage-csi-driver/actions/workflows/docker-hub.yml) ## Overview -The Container Storage Interface ([CSI](https://github.com/container-storage-interface/spec)) Driver for Linode Block Storage enables container orchestrators such as Kubernetes to manage the life-cycle of persistent storage claims. +The Container Storage Interface +([CSI](https://github.com/container-storage-interface/spec)) Driver for Linode +Block Storage enables container orchestrators such as Kubernetes to manage the +life-cycle of persistent storage claims. -More information about the Kubernetes CSI can be found in the GitHub [Kubernetes CSI](https://kubernetes-csi.github.io/docs/example.html) and [CSI Spec](https://github.com/container-storage-interface/spec/) repos. +More information about the Kubernetes CSI can be found in the GitHub [Kubernetes +CSI](https://kubernetes-csi.github.io/docs/example.html) and [CSI +Spec](https://github.com/container-storage-interface/spec/) repositories. ## Deployment -There are two ways of deploying CSI. The recommended way is to use Helm and second way is to use manually set secrets on the kubernetes cluster then use kubectl to deploy using the given [`yaml` file](https://raw.githubusercontent.com/linode/linode-blockstorage-csi-driver/master/pkg/linode-bs/deploy/releases/linode-blockstorage-csi-driver.yaml). +There are two ways of deploying CSI. +The recommended way is to use Helm. +The second way is to use manually set secrets on the kubernetes cluster then use +kubectl to deploy using the given [`yaml` +file](https://raw.githubusercontent.com/linode/linode-blockstorage-csi-driver/master/pkg/linode-bs/deploy/releases/linode-blockstorage-csi-driver.yaml). ### Requirements * Kubernetes v1.16+ -* [LINODE_API_TOKEN](https://cloud.linode.com/profile/tokens) (See 'Secure a Linode API Access Token' below). +* [LINODE_API_TOKEN](https://cloud.linode.com/profile/tokens) (See 'Secure a + Linode API Access Token' below). * Linode [REGION](https://api.linode.com/v4/regions). ### Secure a Linode API Access Token: -Generate a Personal Access Token (PAT) using the [Linode Cloud Manager](https://cloud.linode.com/profile/tokens). +Generate a Personal Access Token (PAT) using the [Linode Cloud +Manager](https://cloud.linode.com/profile/tokens). This token will need: @@ -26,62 +37,79 @@ This token will need: * A sufficient "Expiry" to allow for continued use of your volumes ### [Recommended] Using Helm to deploy the CSI Driver -LINODE_API_TOKEN must be a Linode APIv4 [Personal Access Token](https://cloud.linode.com/profile/tokens) with Volume and Linode permissions. -REGION must be a Linode [region](https://api.linode.com/v4/regions). +`LINODE_API_TOKEN` must be a Linode APIv4 [Personal Access +Token](https://cloud.linode.com/profile/tokens) with Volume and Linode +permissions. + +`REGION` must be a Linode [region](https://api.linode.com/v4/regions). #### Install the csi-linode repo -```shell + +```sh helm repo add linode-csi https://linode.github.io/linode-blockstorage-csi-driver/ helm repo update linode-csi ``` #### To deploy the CSI Driver + ```sh -export LINODE_API_TOKEN= -export REGION= +export LINODE_API_TOKEN="...your Linode API token..." +export REGION="your preferred region" -helm install linode-csi-driver --set apiToken=$LINODE_API_TOKEN,region=$REGION linode-csi/linode-blockstorage-csi-driver +helm install linode-csi-driver \ + --set apiToken="${LINODE_API_TOKEN}" \ + --set region="${REGION}" \ + linode-csi/linode-blockstorage-csi-driver ``` _See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._ -#### To uninstall linode-csi-driver from kubernetes cluster. Run the following command: +#### Uninstalling linode-blockstorage-csi-driver + +To uninstall the linode-blockstorage-csi-driver from your cluster, run the +following command: + ```sh helm uninstall linode-csi-driver ``` -_See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation._ +_See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command +documentation._ -#### To upgrade when new changes are made to the helm chart. Run the following command: -```sh -export LINODE_API_TOKEN= -export REGION= +#### Upgrading linode-blockstorage-csi-driver -helm upgrade linode-csi-driver --install --set apiToken=$LINODE_API_TOKEN,region=$REGION linode-csi/linode-blockstorage-csi-driver +To upgrade when new changes are made to the helm chart, run the following +command: + +```sh +export LINODE_API_TOKEN="...your Linode API token..." +export REGION="your preferred region" + +helm upgrade linode-csi-driver \ + --install \ + --set apiToken="${LINODE_API_TOKEN}" \ + --set region="${REGION}" \ + linode-csi/linode-blockstorage-csi-driver ``` -_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation._ +_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command +documentation._ #### Configurations -There are other variables that can be set to a different value. For list of all the modifiable variables/values, take a look at './helm-chart/csi-driver/values.yaml'. +There are other variables that can be set to a different value. +For list of all the modifiable variables/values, see +[helm-chart/csi-driver/values.yaml](https://github.com/linode/linode-blockstorage-csi-driver/blob/main/helm-chart/csi-driver/values.yaml). -Values can be set/overrided by using the '--set var=value,...' flag or by passing in a custom-values.yaml using '-f custom-values.yaml'. +Values can be set/overrided by using the '--set var=value,...' flag or by +passing in a custom-values.yaml using '-f custom-values.yaml'. -Recommendation: Use custom-values.yaml to override the variables to avoid any errors with template rendering +Recommendation: Use a custom `values.yaml` file to override the variables to +avoid any errors with template rendering. +### Deploying with kubectl -### Using Kubectl to deploy CSI +#### Create a secret -#### Create a kubernetes secret: - -Run the following commands to stash a `LINODE_TOKEN` in your Kubernetes cluster: - -```bash -read -s -p "Linode API Access Token: " LINODE_TOKEN -read -p "Linode Region of Cluster: " LINODE_REGION -cat < /data/example.txt; ls -l /data" total 20 -rw-r--r-- 1 root root 12 Dec 5 13:06 example.txt drwx------ 2 root root 16384 Dec 5 06:03 lost+found +``` +Then we will delete and recreate the pod, and check to make sure our data is +still there: + +```sh $ kubectl delete pods/csi-example-pod pod "csi-example-pod" deleted @@ -181,18 +254,30 @@ persistence ``` ### Encrypted Drives using LUKS -The ability to encrypt a PVC with a user owned secret provides an additional security layer that gives control of the data to the cluster owner instead of the platform provider. + +The ability to encrypt a PVC with a user owned secret provides an additional +security layer that gives control of the data to the cluster owner instead of +the platform provider. #### Notes + 1. Resize is possible with similar steps to resizing PVCs on LKE and are not handled by driver. Need cryptSetup resize + resize2fs on LKE node. -2. Key rotation process is not handled by driver but is possible via similar steps to out of band resize operations. +2. Key rotation process is not handled by driver but is possible via similar + steps to out of band resize operations. 3. Encryption is only possible on a new/empty PVC. -4. LUKS key is currently pulled from a native Kubernetes secret. Take note of how your cluster handles secrets in etcd. The CSI driver is careful to otherwise keep the secret on an ephemeral tmpfs mount and otherwise refuses to continue. +4. LUKS key is currently pulled from a native Kubernetes secret. + Take note of how your cluster handles secrets in etcd. + The CSI driver is careful to otherwise keep the secret on an ephemeral tmpfs + mount and otherwise refuses to continue. #### Example StorageClass -*Note* To use an encryption key per PVC you can make a new StorageClass/Secret combination each time. -``` + +> [!TIP] +> To use an encryption key per PVC you can make a new StorageClass/Secret +> combination each time. + +```yaml allowVolumeExpansion: true apiVersion: storage.k8s.io/v1 kind: StorageClass @@ -219,9 +304,9 @@ stringData: luksKey: "SECRETGOESHERE" ``` -#### Example PVC. +#### Example PVC -``` +```yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: @@ -247,11 +332,15 @@ spec: storageClassName: linode-block-storage-retain-luks ``` ### Adding Tags to created volumes -This feature gives users the ability to add tags to volumes created by a specific storageClass, to allow for better tracking of volumes. -Tags are added as a comma seperated string value for a parameter `linodebs.csi.linode.com/volumeTags` + +This feature gives users the ability to add tags to volumes created by a +specific storageClass, to allow for better tracking of volumes. +Tags are added as a comma seperated string value for a parameter +`linodebs.csi.linode.com/volumeTags` #### Example StorageClass -``` + +```yaml allowVolumeExpansion: true apiVersion: storage.k8s.io/v1 kind: StorageClass @@ -268,15 +357,28 @@ parameters: ## Disclaimers -* Until this driver has reached v1.0.0 it may not maintain compatibility between driver versions -* Requests for Persistent Volumes with a `require_size` less than the Linode minimum Block Storage size will be fulfilled with a Linode Block Storage volume of the minimum size (currently 10GiB), this is [in accordance with the CSI specification](https://github.com/container-storage-interface/spec/blob/v1.0.0/spec.md#createvolume). The upper-limit size constraint (`limit_bytes`) will also be honored so the size of Linode Block Storage volumes provisioned will not exceed this parameter. +* Until this driver has reached v1.0.0 it may not maintain compatibility between + driver versions. +* Requests for Persistent Volumes with a `require_size` less than the Linode + minimum Block Storage size will be fulfilled with a Linode Block Storage volume + of the minimum size (currently `10Gi`). + This is [in accordance with the CSI + specification](https://github.com/container-storage-interface/spec/blob/v1.0.0/spec.md#createvolume). + The upper-limit size constraint (`limit_bytes`) will also be honored so the + size of Linode Block Storage volumes provisioned will not exceed this + parameter. ## Contribution Guidelines -Want to improve the linode-blockstorage-csi-driver? Please start [here](.github/CONTRIBUTING.md). +Want to improve the linode-blockstorage-csi-driver? +Please start [here](.github/CONTRIBUTING.md). ## Join us on Slack -For general help or discussion, join the [Kubernetes Slack](http://slack.k8s.io/) channel [#linode](https://kubernetes.slack.com/messages/CD4B15LUR). +For general help or discussion, join the [Kubernetes +Slack](http://slack.k8s.io/) channel +[#linode](https://kubernetes.slack.com/messages/CD4B15LUR). -For development and debugging, join the [Gopher's Slack](https://invite.slack.golangbridge.org/) channel [#linodego](https://gophers.slack.com/messages/CAG93EB2S). +For development and debugging, join the [Gopher's +Slack](https://invite.slack.golangbridge.org/) channel +[#linodego](https://gophers.slack.com/messages/CAG93EB2S). diff --git a/deploy/kubernetes/base/ds-csi-linode-node.yaml b/deploy/kubernetes/base/ds-csi-linode-node.yaml index a486b99b..6e8981e7 100644 --- a/deploy/kubernetes/base/ds-csi-linode-node.yaml +++ b/deploy/kubernetes/base/ds-csi-linode-node.yaml @@ -57,15 +57,11 @@ spec: - name: csi-linode-plugin image: linode/linode-blockstorage-csi-driver:latest args : - - "--endpoint=$(CSI_ENDPOINT)" - - "--token=$(LINODE_TOKEN)" - - "--url=$(LINODE_API_URL)" - - "--node=$(NODE_NAME)" - "--v=2" env: - name: CSI_ENDPOINT value: unix:///csi/csi.sock - - name: LINODE_API_URL + - name: LINODE_URL value: https://api.linode.com/v4 - name: NODE_NAME valueFrom: diff --git a/deploy/kubernetes/base/ss-csi-linode-controller.yaml b/deploy/kubernetes/base/ss-csi-linode-controller.yaml index 990f0d84..5fc0aae7 100644 --- a/deploy/kubernetes/base/ss-csi-linode-controller.yaml +++ b/deploy/kubernetes/base/ss-csi-linode-controller.yaml @@ -77,16 +77,11 @@ spec: - name: linode-csi-plugin image: linode/linode-blockstorage-csi-driver:latest args: - - "--endpoint=$(CSI_ENDPOINT)" - - "--token=$(LINODE_TOKEN)" - - "--url=$(LINODE_API_URL)" - - "--node=$(NODE_NAME)" - - "--bs-prefix=$(LINODE_BS_PREFIX)" - "--v=2" env: - name: CSI_ENDPOINT value: unix:///var/lib/csi/sockets/pluginproxy/csi.sock - - name: LINODE_API_URL + - name: LINODE_URL value: https://api.linode.com/v4 - name: LINODE_BS_PREFIX - name: NODE_NAME diff --git a/go.mod b/go.mod index b782115d..be300273 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.20 require ( github.com/container-storage-interface/spec v1.3.0 + github.com/ianschenck/envflag v0.0.0-20140720210342-9111d830d133 github.com/linode/go-metadata v0.2.0 github.com/linode/linodego v1.23.0 golang.org/x/net v0.17.0 diff --git a/go.sum b/go.sum index b2b0d336..5e03f0d3 100644 --- a/go.sum +++ b/go.sum @@ -59,6 +59,8 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianschenck/envflag v0.0.0-20140720210342-9111d830d133 h1:h6FO/Da7rdYqJbRYMW9f+SMBWnJVguWh+0ERefW8zp8= +github.com/ianschenck/envflag v0.0.0-20140720210342-9111d830d133/go.mod h1:pyYc5lldRtL0l5YitYVv1dLKuC0qhMfAfiR7BLsN2pA= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= diff --git a/helm-chart/csi-driver/templates/csi-linode-controller.yaml b/helm-chart/csi-driver/templates/csi-linode-controller.yaml index fdbf5e26..0b0277df 100644 --- a/helm-chart/csi-driver/templates/csi-linode-controller.yaml +++ b/helm-chart/csi-driver/templates/csi-linode-controller.yaml @@ -58,17 +58,14 @@ spec: - mountPath: /var/lib/csi/sockets/pluginproxy/ name: socket-dir - args: - - --endpoint=$(CSI_ENDPOINT) - - --token=$(LINODE_TOKEN) - - --url=$(LINODE_API_URL) - - --bs-prefix=$(LINODE_BS_PREFIX) - --v=2 env: - name: CSI_ENDPOINT value: unix:///var/lib/csi/sockets/pluginproxy/csi.sock - - name: LINODE_API_URL + - name: LINODE_URL value: https://api.linode.com/v4 - - name: LINODE_BS_PREFIX + - name: LINODE_VOLUME_LABEL_PREFIX + value: {{ .Values.volumeLabelPrefix | default "" | quote }} - name: NODE_NAME valueFrom: fieldRef: diff --git a/helm-chart/csi-driver/templates/daemonset.yaml b/helm-chart/csi-driver/templates/daemonset.yaml index 9521c291..0ee1f370 100644 --- a/helm-chart/csi-driver/templates/daemonset.yaml +++ b/helm-chart/csi-driver/templates/daemonset.yaml @@ -37,14 +37,11 @@ spec: - mountPath: /registration name: registration-dir - args: - - --endpoint=$(CSI_ENDPOINT) - - --token=$(LINODE_TOKEN) - - --url=$(LINODE_API_URL) - --v=2 env: - name: CSI_ENDPOINT value: unix:///csi/csi.sock - - name: LINODE_API_URL + - name: LINODE_URL value: https://api.linode.com/v4 - name: NODE_NAME valueFrom: diff --git a/helm-chart/csi-driver/values.yaml b/helm-chart/csi-driver/values.yaml index 10608d3e..3c38f145 100644 --- a/helm-chart/csi-driver/values.yaml +++ b/helm-chart/csi-driver/values.yaml @@ -5,6 +5,10 @@ apiToken: "" # region [Required if secretRef is not set] - Must be a Linode region. (https://api.linode.com/v4/regions) region: "" +# (OPTIONAL) Label prefix for the Linode Block Storage volumes created by this +# driver. +volumeLabelPrefix: "" + # Default namespace is "kube-system" but it can be set to another namespace namespace: kube-system diff --git a/main.go b/main.go index 41bfa8a8..d6df543f 100644 --- a/main.go +++ b/main.go @@ -20,30 +20,56 @@ import ( "fmt" "os" + "github.com/ianschenck/envflag" + "github.com/linode/linodego" + "k8s.io/klog/v2" + driver "github.com/linode/linode-blockstorage-csi-driver/pkg/linode-bs" linodeclient "github.com/linode/linode-blockstorage-csi-driver/pkg/linode-client" mountmanager "github.com/linode/linode-blockstorage-csi-driver/pkg/mount-manager" - "k8s.io/klog/v2" ) -const ( - driverName = "linodebs.csi.linode.com" -) +const driverName = "linodebs.csi.linode.com" -var ( - vendorVersion string - endpoint = flag.String("endpoint", "unix:/tmp/csi.sock", "CSI endpoint") - token = flag.String("token", "", "Linode API Token") - url = flag.String("url", "", "Linode API URL") - bsPrefix = flag.String("bs-prefix", "", "Linode BlockStorage Volume label prefix") -) +var vendorVersion string // set by the linker -func init() { - _ = flag.Set("logtostderr", "true") +type configuration struct { + // The UNIX socket to listen on for RPC requests. + csiEndpoint string + + // Linode personal access token, used to make requests to the Linode + // API. + linodeToken string + + // Linode API URL. + linodeURL string + + // Optional label prefix to use when creating new Linode Block Storage + // Volumes. + volumeLabelPrefix string + + // Name of the current node, when running as the node plugin. + // + // deprecated: This is not needed as the CSI driver now uses the Linode + // Metadata Service to source information about the current + // node/instance. It will be removed in a future change. + nodeName string +} + +func loadConfig() configuration { + var cfg configuration + envflag.StringVar(&cfg.csiEndpoint, "CSI_ENDPOINT", "unix:/tmp/csi.sock", "Path to the CSI endpoint socket") + envflag.StringVar(&cfg.linodeToken, "LINODE_TOKEN", "", "Linode API token") + envflag.StringVar(&cfg.linodeURL, "LINODE_URL", linodego.APIHost, "Linode API URL") + envflag.StringVar(&cfg.volumeLabelPrefix, "LINODE_VOLUME_LABEL_PREFIX", "", "Linode Block Storage volume label prefix") + envflag.StringVar(&cfg.nodeName, "NODE_NAME", "", "Name of the current node") // deprecated + envflag.Parse() + return cfg } func main() { klog.InitFlags(nil) + _ = flag.Set("logtostderr", "true") flag.Parse() if err := handle(); err != nil { klog.Fatal(err) @@ -55,16 +81,18 @@ func handle() error { if vendorVersion == "" { return errors.New("vendorVersion must be set at compile time") } - if *token == "" { + klog.V(4).Infof("Driver vendor version %v", vendorVersion) + + cfg := loadConfig() + if cfg.linodeToken == "" { return errors.New("linode token required") } - klog.V(4).Infof("Driver vendor version %v", vendorVersion) linodeDriver := driver.GetLinodeDriver() // Initialize Linode Driver (Move setup to main?) uaPrefix := fmt.Sprintf("LinodeCSI/%s", vendorVersion) - cloudProvider, err := linodeclient.NewLinodeClient(*token, uaPrefix, *url) + cloudProvider, err := linodeclient.NewLinodeClient(cfg.linodeToken, uaPrefix, cfg.linodeURL) if err != nil { return fmt.Errorf("failed to set up linode client: %s", err) } @@ -74,16 +102,12 @@ func handle() error { metadata, err := driver.GetMetadata(context.Background()) if err != nil { + klog.ErrorS(err, "Metadata service not available, falling back to API") if metadata, err = driver.GetMetadataFromAPI(context.Background(), cloudProvider); err != nil { return fmt.Errorf("get metadata from api: %w", err) } } - prefix := "" - if bsPrefix != nil { - prefix = *bsPrefix - } - if err := linodeDriver.SetupLinodeDriver( cloudProvider, mounter, @@ -91,11 +115,11 @@ func handle() error { metadata, driverName, vendorVersion, - prefix, + cfg.volumeLabelPrefix, ); err != nil { - return fmt.Errorf("failed to initialize Linode CSI Driver: %v", err) + return fmt.Errorf("setup driver: %v", err) } - linodeDriver.Run(*endpoint) + linodeDriver.Run(cfg.csiEndpoint) return nil }