From c03bdbd7d3b94a8e40bc4ca37d3e046efbec0eca Mon Sep 17 00:00:00 2001 From: kane8n Date: Fri, 26 Jan 2024 20:38:12 +0900 Subject: [PATCH 1/8] add pv support --- api/v1alpha1/gatling_types.go | 16 + api/v1alpha1/zz_generated.deepcopy.go | 16 + ...tling-operator.tech.zozo.com_gatlings.yaml | 2958 +++++++++++++++++ controllers/gatling_controller.go | 143 +- 4 files changed, 3081 insertions(+), 52 deletions(-) diff --git a/api/v1alpha1/gatling_types.go b/api/v1alpha1/gatling_types.go index 2bd99c0..5829937 100644 --- a/api/v1alpha1/gatling_types.go +++ b/api/v1alpha1/gatling_types.go @@ -54,6 +54,14 @@ type GatlingSpec struct { // +kubebuilder:validation:Optional CloudStorageSpec CloudStorageSpec `json:"cloudStorageSpec,omitempty"` + // (Optional) PersistentVolume specification. + // +kubebuilder:validation:Optional + PersistentVolume corev1.PersistentVolume `json:"persistentVolume,omitempty"` + + // (Optional) PersistentVolumeClaim specification. + // +kubebuilder:validation:Optional + PersistentVolumeClaim corev1.PersistentVolumeClaim `json:"persistentVolumeClaim,omitempty"` + // (Optional) Notification Service specification. // +kubebuilder:validation:Optional NotificationServiceSpec NotificationServiceSpec `json:"notificationServiceSpec,omitempty"` @@ -88,6 +96,10 @@ type PodSpec struct { // (Required) ServiceAccountName specification. // +kubebuilder:validation:Required ServiceAccountName string `json:"serviceAccountName"` + + // (Optional) volumes specification. + // +kubebuilder:validation:Optional + Volumes []corev1.Volume `json:"volumes,omitempty"` } // TestScenarioSpec defines the load testing scenario @@ -133,6 +145,10 @@ type TestScenarioSpec struct { // (Optional) Environment variables used for running load testing scenario. // +optional Env []corev1.EnvVar `json:"env,omitempty"` + + // (Optional) Pod volumes to mount into the container's filesystem. + // +kubebuilder:validation:Optional + VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"` } // CloudStorageSpec defines Cloud Storage Provider specification. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index d80112b..35c5643 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -112,6 +112,8 @@ func (in *GatlingSpec) DeepCopyInto(out *GatlingSpec) { *out = *in in.PodSpec.DeepCopyInto(&out.PodSpec) in.CloudStorageSpec.DeepCopyInto(&out.CloudStorageSpec) + in.PersistentVolume.DeepCopyInto(&out.PersistentVolume) + in.PersistentVolumeClaim.DeepCopyInto(&out.PersistentVolumeClaim) out.NotificationServiceSpec = in.NotificationServiceSpec in.TestScenarioSpec.DeepCopyInto(&out.TestScenarioSpec) } @@ -168,6 +170,13 @@ func (in *PodSpec) DeepCopyInto(out *PodSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]v1.Volume, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodSpec. @@ -211,6 +220,13 @@ func (in *TestScenarioSpec) DeepCopyInto(out *TestScenarioSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.VolumeMounts != nil { + in, out := &in.VolumeMounts, &out.VolumeMounts + *out = make([]v1.VolumeMount, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TestScenarioSpec. diff --git a/config/crd/bases/gatling-operator.tech.zozo.com_gatlings.yaml b/config/crd/bases/gatling-operator.tech.zozo.com_gatlings.yaml index ca1acb5..2d17408 100644 --- a/config/crd/bases/gatling-operator.tech.zozo.com_gatlings.yaml +++ b/config/crd/bases/gatling-operator.tech.zozo.com_gatlings.yaml @@ -215,6 +215,1368 @@ spec: description: (Optional) The flag of notifying gatling report. Defaults to `false` type: boolean + persistentVolume: + description: (Optional) PersistentVolume specification. + 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: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: 'spec defines a specification of a persistent volume + owned by the cluster. Provisioned by an administrator. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistent-volumes' + properties: + accessModes: + description: 'accessModes contains all ways the volume can + be mounted. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes' + items: + type: string + type: array + awsElasticBlockStore: + description: 'awsElasticBlockStore represents an AWS Disk + resource that is attached to a kubelet''s host machine and + then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'partition is the partition in the volume + that you want to mount. If omitted, the default is to + mount by volume name. Examples: For volume /dev/sda1, + you specify the partition as "1". Similarly, the volume + partition for /dev/sda is "0" (or you can leave the + property empty).' + format: int32 + type: integer + readOnly: + description: 'readOnly value true will force the readOnly + setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'volumeID is unique ID of the persistent + disk resource in AWS (Amazon EBS volume). More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: azureDisk represents an Azure Data Disk mount + on the host and bind mount to the pod. + properties: + cachingMode: + description: 'cachingMode is the Host Caching mode: None, + Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the data disk in + the blob storage + type: string + diskURI: + description: diskURI is the URI of data disk in the blob + storage + type: string + fsType: + description: fsType is Filesystem type to mount. Must + be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + kind: + description: 'kind expected values are Shared: multiple + blob disks per storage account Dedicated: single blob + disk per storage account Managed: azure managed data + disk (only in managed availability set). defaults to + shared' + type: string + readOnly: + description: readOnly Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: azureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: readOnly defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of secret that contains + Azure Storage Account Name and Key + type: string + secretNamespace: + description: secretNamespace is the namespace of the secret + that contains Azure Storage Account Name and Key default + is the same as the Pod + type: string + shareName: + description: shareName is the azure Share Name + type: string + required: + - secretName + - shareName + type: object + capacity: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'capacity is the description of the persistent + volume''s resources and capacity. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#capacity' + type: object + cephfs: + description: cephFS represents a Ceph FS mount on the host + that shares a pod's lifetime + properties: + monitors: + description: 'monitors is Required: Monitors is a collection + of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'path is Optional: Used as the mounted root, + rather than the full Ceph tree, default is /' + type: string + readOnly: + description: 'readOnly is Optional: Defaults to false + (read/write). ReadOnly here will force the ReadOnly + setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'secretFile is Optional: SecretFile is the + path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'secretRef is Optional: SecretRef is reference + to the authentication secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: name is unique within a namespace to + reference a secret resource. + type: string + namespace: + description: namespace defines the space within which + the secret name must be unique. + type: string + type: object + user: + description: 'user is Optional: User is the rados user + name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'cinder represents a cinder volume attached and + mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'fsType Filesystem type to mount. Must be + a filesystem type supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'readOnly is Optional: Defaults to false + (read/write). ReadOnly here will force the ReadOnly + setting in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'secretRef is Optional: points to a secret + object containing parameters used to connect to OpenStack.' + properties: + name: + description: name is unique within a namespace to + reference a secret resource. + type: string + namespace: + description: namespace defines the space within which + the secret name must be unique. + type: string + type: object + volumeID: + description: 'volumeID used to identify the volume in + cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + claimRef: + description: 'claimRef is part of a bi-directional binding + between PersistentVolume and PersistentVolumeClaim. Expected + to be non-nil when bound. claim.VolumeName is the authoritative + bind between PV and PVC. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#binding' + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead + of an entire object, this string should contain a valid + JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container + within a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that + triggered the event) or if no container name is specified + "spec.containers[2]" (container with index 2 in this + pod). This syntax is chosen only to have some well-defined + way of referencing a part of an object. TODO: this design + is not final and this field is subject to change in + the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference + is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + csi: + description: csi represents storage that is handled by an + external CSI driver (Beta feature). + properties: + controllerExpandSecretRef: + description: controllerExpandSecretRef is a reference + to the secret object containing sensitive information + to pass to the CSI driver to complete the CSI ControllerExpandVolume + call. This is an alpha field and requires enabling ExpandCSIVolumes + feature gate. This field is optional, and may be empty + if no secret is required. If the secret object contains + more than one secret, all secrets are passed. + properties: + name: + description: name is unique within a namespace to + reference a secret resource. + type: string + namespace: + description: namespace defines the space within which + the secret name must be unique. + type: string + type: object + controllerPublishSecretRef: + description: controllerPublishSecretRef is a reference + to the secret object containing sensitive information + to pass to the CSI driver to complete the CSI ControllerPublishVolume + and ControllerUnpublishVolume calls. This field is optional, + and may be empty if no secret is required. If the secret + object contains more than one secret, all secrets are + passed. + properties: + name: + description: name is unique within a namespace to + reference a secret resource. + type: string + namespace: + description: namespace defines the space within which + the secret name must be unique. + type: string + type: object + driver: + description: driver is the name of the driver to use for + this volume. Required. + type: string + fsType: + description: fsType to mount. Must be a filesystem type + supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". + type: string + nodePublishSecretRef: + description: nodePublishSecretRef is a reference to the + secret object containing sensitive information to pass + to the CSI driver to complete the CSI NodePublishVolume + and NodeUnpublishVolume calls. This field is optional, + and may be empty if no secret is required. If the secret + object contains more than one secret, all secrets are + passed. + properties: + name: + description: name is unique within a namespace to + reference a secret resource. + type: string + namespace: + description: namespace defines the space within which + the secret name must be unique. + type: string + type: object + nodeStageSecretRef: + description: nodeStageSecretRef is a reference to the + secret object containing sensitive information to pass + to the CSI driver to complete the CSI NodeStageVolume + and NodeStageVolume and NodeUnstageVolume calls. This + field is optional, and may be empty if no secret is + required. If the secret object contains more than one + secret, all secrets are passed. + properties: + name: + description: name is unique within a namespace to + reference a secret resource. + type: string + namespace: + description: namespace defines the space within which + the secret name must be unique. + type: string + type: object + readOnly: + description: readOnly value to pass to ControllerPublishVolumeRequest. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: volumeAttributes of the volume to publish. + type: object + volumeHandle: + description: volumeHandle is the unique volume name returned + by the CSI volume plugin’s CreateVolume to refer to + the volume on all subsequent calls. Required. + type: string + required: + - driver + - volumeHandle + type: object + fc: + description: fc represents a Fibre Channel resource that is + attached to a kubelet's host machine and then exposed to + the pod. + properties: + fsType: + description: 'fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. TODO: how do we prevent + errors in the filesystem from compromising the machine' + type: string + lun: + description: 'lun is Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'readOnly is Optional: Defaults to false + (read/write). ReadOnly here will force the ReadOnly + setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC target worldwide + names (WWNs)' + items: + type: string + type: array + wwids: + description: 'wwids Optional: FC volume world wide identifiers + (wwids) Either wwids or combination of targetWWNs and + lun must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: flexVolume represents a generic volume resource + that is provisioned/attached using an exec based plugin. + properties: + driver: + description: driver is the name of the driver to use for + this volume. + type: string + fsType: + description: fsType is the Filesystem type to mount. Must + be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". The default filesystem + depends on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'options is Optional: this field holds extra + command options if any.' + type: object + readOnly: + description: 'readOnly is Optional: defaults to false + (read/write). ReadOnly here will force the ReadOnly + setting in VolumeMounts.' + type: boolean + secretRef: + description: 'secretRef is Optional: SecretRef is reference + to the secret object containing sensitive information + to pass to the plugin scripts. This may be empty if + no secret object is specified. If the secret object + contains more than one secret, all secrets are passed + to the plugin scripts.' + properties: + name: + description: name is unique within a namespace to + reference a secret resource. + type: string + namespace: + description: namespace defines the space within which + the secret name must be unique. + type: string + type: object + required: + - driver + type: object + flocker: + description: flocker represents a Flocker volume attached + to a kubelet's host machine and exposed to the pod for its + usage. This depends on the Flocker control service being + running + properties: + datasetName: + description: datasetName is Name of the dataset stored + as metadata -> name on the dataset for Flocker should + be considered as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of the dataset. This + is unique identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'gcePersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'fsType is filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'partition is the partition in the volume + that you want to mount. If omitted, the default is to + mount by volume name. Examples: For volume /dev/sda1, + you specify the partition as "1". Similarly, the volume + partition for /dev/sda is "0" (or you can leave the + property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'pdName is unique name of the PD resource + in GCE. Used to identify the disk in GCE. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + glusterfs: + description: 'glusterfs represents a Glusterfs volume that + is attached to a host and exposed to the pod. Provisioned + by an admin. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'endpoints is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + endpointsNamespace: + description: 'endpointsNamespace is the namespace that + contains Glusterfs endpoint. If this field is empty, + the EndpointNamespace defaults to the same namespace + as the bound PVC. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'path is the Glusterfs volume path. More + info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'readOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to + false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'hostPath represents a directory on the host. + Provisioned by a developer or tester. This is useful for + single-node development and testing only! On-host storage + is not supported in any way and WILL NOT WORK in a multi-node + cluster. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + properties: + path: + description: 'path of the directory on the host. If the + path is a symlink, it will follow the link to the real + path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'type for HostPath Volume Defaults to "" + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: iscsi represents an ISCSI Disk resource that + is attached to a kubelet's host machine and then exposed + to the pod. Provisioned by an admin. + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether support + iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether support iSCSI + Session CHAP authentication + type: boolean + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + initiatorName: + description: initiatorName is the custom iSCSI Initiator + Name. If initiatorName is specified with iscsiInterface + simultaneously, new iSCSI interface : will be created for the connection. + type: string + iqn: + description: iqn is Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iscsiInterface is the interface Name that + uses an iSCSI transport. Defaults to 'default' (tcp). + type: string + lun: + description: lun is iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: portals is the iSCSI Target Portal List. + The Portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret for iSCSI target + and initiator authentication + properties: + name: + description: name is unique within a namespace to + reference a secret resource. + type: string + namespace: + description: namespace defines the space within which + the secret name must be unique. + type: string + type: object + targetPortal: + description: targetPortal is iSCSI Target Portal. The + Portal is either an IP or ip_addr:port if the port is + other than default (typically TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + local: + description: local represents directly-attached storage with + node affinity + properties: + fsType: + description: fsType is the filesystem type to mount. It + applies only when the Path is a block device. Must be + a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". The default value is to auto-select + a filesystem if unspecified. + type: string + path: + description: path of the full path to the volume on the + node. It can be either a directory or block device (disk, + partition, ...). + type: string + required: + - path + type: object + mountOptions: + description: 'mountOptions is the list of mount options, e.g. + ["ro", "soft"]. Not validated - mount will simply fail if + one is invalid. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#mount-options' + items: + type: string + type: array + nfs: + description: 'nfs represents an NFS mount on the host. Provisioned + by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'path that is exported by the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'readOnly here will force the NFS export + to be mounted with read-only permissions. Defaults to + false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'server is the hostname or IP address of + the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + nodeAffinity: + description: nodeAffinity defines constraints that limit what + nodes this volume can be accessed from. This field influences + the scheduling of pods that use this volume. + properties: + required: + description: required specifies hard node constraints + that must be met. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term + matches no objects. The requirements of them are + ANDed. The TopologySelectorTerm type implements + a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + persistentVolumeReclaimPolicy: + description: 'persistentVolumeReclaimPolicy defines what happens + to a persistent volume when released from its claim. Valid + options are Retain (default for manually created PersistentVolumes), + Delete (default for dynamically provisioned PersistentVolumes), + and Recycle (deprecated). Recycle must be supported by the + volume plugin underlying this PersistentVolume. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#reclaiming' + type: string + photonPersistentDisk: + description: photonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + pdID: + description: pdID is the ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: portworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: fSType represents the filesystem type to + mount Must be a filesystem type supported by the host + operating system. Ex. "ext4", "xfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + quobyte: + description: quobyte represents a Quobyte mount on the host + that shares a pod's lifetime + properties: + group: + description: group to map volume access to Default is + no group + type: string + readOnly: + description: readOnly here will force the Quobyte volume + to be mounted with read-only permissions. Defaults to + false. + type: boolean + registry: + description: registry represents a single or multiple + Quobyte Registry services specified as a string as host:port + pair (multiple entries are separated with commas) which + acts as the central registry for volumes + type: string + tenant: + description: tenant owning the given Quobyte volume in + the Backend Used with dynamically provisioned Quobyte + volumes, value is set by the plugin + type: string + user: + description: user to map volume access to Defaults to + serivceaccount user + type: string + volume: + description: volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'rbd represents a Rados Block Device mount on + the host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + image: + description: 'image is the rados image name. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'pool is the rados pool name. Default is + rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'secretRef is name of the authentication + secret for RBDUser. If provided overrides keyring. Default + is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: name is unique within a namespace to + reference a secret resource. + type: string + namespace: + description: namespace defines the space within which + the secret name must be unique. + type: string + type: object + user: + description: 'user is the rados user name. Default is + admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: scaleIO represents a ScaleIO persistent volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Default is "xfs" + type: string + gateway: + description: gateway is the host address of the ScaleIO + API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name of the ScaleIO + Protection Domain for the configured storage. + type: string + readOnly: + description: readOnly defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef references to the secret for ScaleIO + user and other sensitive information. If this is not + provided, Login operation will fail. + properties: + name: + description: name is unique within a namespace to + reference a secret resource. + type: string + namespace: + description: namespace defines the space within which + the secret name must be unique. + type: string + type: object + sslEnabled: + description: sslEnabled is the flag to enable/disable + SSL communication with Gateway, default false + type: boolean + storageMode: + description: storageMode indicates whether the storage + for a volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO Storage Pool associated + with the protection domain. + type: string + system: + description: system is the name of the storage system + as configured in ScaleIO. + type: string + volumeName: + description: volumeName is the name of a volume already + created in the ScaleIO system that is associated with + this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + storageClassName: + description: storageClassName is the name of StorageClass + to which this persistent volume belongs. Empty value means + that this volume does not belong to any StorageClass. + type: string + storageos: + description: 'storageOS represents a StorageOS volume that + is attached to the kubelet''s host machine and mounted into + the pod More info: https://examples.k8s.io/volumes/storageos/README.md' + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef specifies the secret to use for + obtaining the StorageOS API credentials. If not specified, + default values will be attempted. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object + instead of an entire object, this string should + contain a valid JSON/Go field access statement, + such as desiredState.manifest.containers[2]. For + example, if the object reference is to a container + within a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container + that triggered the event) or if no container name + is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part + of an object. TODO: this design is not final and + this field is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this + reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + volumeName: + description: volumeName is the human-readable name of + the StorageOS volume. Volume names are only unique + within a namespace. + type: string + volumeNamespace: + description: volumeNamespace specifies the scope of the + volume within StorageOS. If no namespace is specified + then the Pod's namespace will be used. This allows + the Kubernetes name scoping to be mirrored within StorageOS + for tighter integration. Set VolumeName to any name + to override the default behaviour. Set to "default" + if you are not using namespaces within StorageOS. Namespaces + that do not pre-exist within StorageOS will be created. + type: string + type: object + volumeMode: + description: volumeMode defines if a volume is intended to + be used with a formatted filesystem or to remain in raw + block state. Value of Filesystem is implied when not included + in spec. + type: string + vsphereVolume: + description: vsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: fsType is filesystem type to mount. Must + be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage Policy Based + Management (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage Policy Based + Management (SPBM) profile name. + type: string + volumePath: + description: volumePath is the path that identifies vSphere + volume vmdk + type: string + required: + - volumePath + type: object + type: object + status: + description: 'status represents the current information/status + for the persistent volume. Populated by the system. Read-only. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistent-volumes' + properties: + message: + description: message is a human-readable message indicating + details about why the volume is in this state. + type: string + phase: + description: 'phase indicates if a volume is available, bound + to a claim, or released by a claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#phase' + type: string + reason: + description: reason is a brief CamelCase string that describes + any failure and is meant for machine parsing and tidy display + in the CLI. + type: string + type: object + type: object + persistentVolumeClaim: + description: (Optional) PersistentVolumeClaim specification. + 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: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: 'spec defines the desired characteristics of a volume + requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + accessModes: + description: 'accessModes contains the desired access modes + the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'dataSource field can be used to specify either: + * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) If the provisioner + or an external controller can support the specified data + source, it will create a new volume based on the contents + of the specified data source. If the AnyVolumeDataSource + feature gate is enabled, this field will always have the + same contents as the DataSourceRef field.' + properties: + apiGroup: + description: APIGroup is the group for the resource being + referenced. If APIGroup is not specified, the specified + Kind must be in the core API group. For any other third-party + types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - kind + - name + type: object + dataSourceRef: + description: 'dataSourceRef specifies the object from which + to populate the volume with data, if a non-empty volume + is desired. This may be any local object from a non-empty + API group (non core object) or a PersistentVolumeClaim object. + When this field is specified, volume binding will only succeed + if the type of the specified object matches some installed + volume populator or dynamic provisioner. This field will + replace the functionality of the DataSource field and as + such if both fields are non-empty, they must have the same + value. For backwards compatibility, both fields (DataSource + and DataSourceRef) will be set to the same value automatically + if one of them is empty and the other is non-empty. There + are two important differences between DataSource and DataSourceRef: + * While DataSource only allows two specific types of objects, + DataSourceRef allows any non-core object, as well as PersistentVolumeClaim + objects. * While DataSource ignores disallowed values (dropping + them), DataSourceRef preserves all values, and generates + an error if a disallowed value is specified. (Beta) Using + this field requires the AnyVolumeDataSource feature gate + to be enabled.' + properties: + apiGroup: + description: APIGroup is the group for the resource being + referenced. If APIGroup is not specified, the specified + Kind must be in the core API group. For any other third-party + types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - kind + - name + type: object + resources: + description: 'resources represents the minimum resources the + volume should have. If RecoverVolumeExpansionFailure feature + is enabled users are allowed to specify resource requirements + that are lower than previous value but must still be higher + than capacity recorded in the status field of the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of + compute resources required. If Requests is omitted for + a container, it defaults to Limits if that is explicitly + specified, otherwise to an implementation-defined value. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + selector: + description: selector is a label query over volumes to consider + for binding. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists or + DoesNotExist, the values array must be empty. + This array is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field is + "key", the operator is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + storageClassName: + description: 'storageClassName is the name of the StorageClass + required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type of volume is required + by the claim. Value of Filesystem is implied when not included + in claim spec. + type: string + volumeName: + description: volumeName is the binding reference to the PersistentVolume + backing this claim. + type: string + type: object + status: + description: 'status represents the current information/status + of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + accessModes: + description: 'accessModes contains the actual access modes + the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + allocatedResources: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: allocatedResources is the storage resource within + AllocatedResources tracks the capacity allocated to a PVC. + It may be larger than the actual capacity when a volume + expansion operation is requested. For storage quota, the + larger value from allocatedResources and PVC.spec.resources + is used. If allocatedResources is not set, PVC.spec.resources + alone is used for quota calculation. If a volume expansion + capacity request is lowered, allocatedResources is only + lowered if there are no expansion operations in progress + and if the actual volume capacity is equal or lower than + the requested capacity. This is an alpha field and requires + enabling RecoverVolumeExpansionFailure feature. + type: object + capacity: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: capacity represents the actual resources of the + underlying volume. + type: object + conditions: + description: conditions is the current Condition of persistent + volume claim. If underlying persistent volume is being resized + then the Condition will be set to 'ResizeStarted'. + items: + description: PersistentVolumeClaimCondition contails details + about state of pvc + properties: + lastProbeTime: + description: lastProbeTime is the time we probed the + condition. + format: date-time + type: string + lastTransitionTime: + description: lastTransitionTime is the time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: message is the human-readable message indicating + details about last transition. + type: string + reason: + description: reason is a unique, this should be a short, + machine understandable string that gives the reason + for condition's last transition. If it reports "ResizeStarted" + that means the underlying persistent volume is being + resized. + type: string + status: + type: string + type: + description: PersistentVolumeClaimConditionType is a + valid value of PersistentVolumeClaimCondition.Type + type: string + required: + - status + - type + type: object + type: array + phase: + description: phase represents the current phase of PersistentVolumeClaim. + type: string + resizeStatus: + description: resizeStatus stores status of resize operation. + ResizeStatus is not set by default but when expansion is + complete resizeStatus is set to empty string by resize controller + or kubelet. This is an alpha field and requires enabling + RecoverVolumeExpansionFailure feature. + type: string + type: object + type: object podSpec: description: (Optional) Gatling Pod specification. properties: @@ -1154,6 +2516,1561 @@ spec: type: string type: object type: array + volumes: + description: (Optional) volumes specification. + items: + description: Volume represents a named volume in a pod that + may be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'awsElasticBlockStore represents an AWS Disk + resource that is attached to a kubelet''s host machine + and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'partition is the partition in the volume + that you want to mount. If omitted, the default is + to mount by volume name. Examples: For volume /dev/sda1, + you specify the partition as "1". Similarly, the volume + partition for /dev/sda is "0" (or you can leave the + property empty).' + format: int32 + type: integer + readOnly: + description: 'readOnly value true will force the readOnly + setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'volumeID is unique ID of the persistent + disk resource in AWS (Amazon EBS volume). More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: azureDisk represents an Azure Data Disk mount + on the host and bind mount to the pod. + properties: + cachingMode: + description: 'cachingMode is the Host Caching mode: + None, Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the data disk in + the blob storage + type: string + diskURI: + description: diskURI is the URI of data disk in the + blob storage + type: string + fsType: + description: fsType is Filesystem type to mount. Must + be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + kind: + description: 'kind expected values are Shared: multiple + blob disks per storage account Dedicated: single + blob disk per storage account Managed: azure managed + data disk (only in managed availability set). defaults + to shared' + type: string + readOnly: + description: readOnly Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: azureFile represents an Azure File Service + mount on the host and bind mount to the pod. + properties: + readOnly: + description: readOnly defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of secret that + contains Azure Storage Account Name and Key + type: string + shareName: + description: shareName is the azure share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: cephFS represents a Ceph FS mount on the host + that shares a pod's lifetime + properties: + monitors: + description: 'monitors is Required: Monitors is a collection + of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'path is Optional: Used as the mounted + root, rather than the full Ceph tree, default is /' + type: string + readOnly: + description: 'readOnly is Optional: Defaults to false + (read/write). ReadOnly here will force the ReadOnly + setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'secretFile is Optional: SecretFile is + the path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'secretRef is Optional: SecretRef is reference + to the authentication secret for User, default is + empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + user: + description: 'user is optional: User is the rados user + name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'cinder represents a cinder volume attached + and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'readOnly defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'secretRef is optional: points to a secret + object containing parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + volumeID: + description: 'volumeID used to identify the volume in + cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: configMap represents a configMap that should + populate this volume + properties: + defaultMode: + description: 'defaultMode is optional: mode bits used + to set permissions on created files by default. Must + be an octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both octal and + decimal values, JSON requires decimal values for mode + bits. Defaults to 0644. Directories within the path + are not affected by this setting. This might be in + conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + items: + description: items if unspecified, each key-value pair + in the Data field of the referenced ConfigMap will + be projected into the volume as a file whose name + is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. If a + key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. + Paths must be relative and may not contain the '..' + path or start with '..'. + items: + description: Maps a string key to a path within a + volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used + to set permissions on this file. Must be an + octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both octal + and decimal values, JSON requires decimal values + for mode bits. If not specified, the volume + defaultMode will be used. This might be in conflict + with other options that affect the file mode, + like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: path is the relative path of the + file to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: optional specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + csi: + description: csi (Container Storage Interface) represents + ephemeral storage that is handled by certain external + CSI drivers (Beta feature). + properties: + driver: + description: driver is the name of the CSI driver that + handles this volume. Consult with your admin for the + correct name as registered in the cluster. + type: string + fsType: + description: fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the + associated CSI driver which will determine the default + filesystem to apply. + type: string + nodePublishSecretRef: + description: nodePublishSecretRef is a reference to + the secret object containing sensitive information + to pass to the CSI driver to complete the CSI NodePublishVolume + and NodeUnpublishVolume calls. This field is optional, + and may be empty if no secret is required. If the + secret object contains more than one secret, all secret + references are passed. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + readOnly: + description: readOnly specifies a read-only configuration + for the volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: volumeAttributes stores driver-specific + properties that are passed to the CSI driver. Consult + your driver's documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: downwardAPI represents downward API about the + pod that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created + files by default. Must be a Optional: mode bits used + to set permissions on created files by default. Must + be an octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both octal and + decimal values, JSON requires decimal values for mode + bits. Defaults to 0644. Directories within the path + are not affected by this setting. This might be in + conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume + file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the + pod: only annotations, labels, name and namespace + are supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in + the specified API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode bits used to set + permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal + values, JSON requires decimal values for mode + bits. If not specified, the volume defaultMode + will be used. This might be in conflict with + other options that affect the file mode, like + fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must not + be absolute or contain the ''..'' path. Must + be utf-8 encoded. The first item of the relative + path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required for + volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of + the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'emptyDir represents a temporary directory + that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'medium represents what type of storage + medium should back this directory. The default is + "" which means to use the node''s default medium. + Must be an empty string (default) or Memory. More + info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: 'sizeLimit is the total amount of local + storage required for this EmptyDir volume. The size + limit is also applicable for memory medium. The maximum + usage on memory medium EmptyDir would be the minimum + value between the SizeLimit specified here and the + sum of memory limits of all containers in a pod. The + default is nil which means that the limit is undefined. + More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: "ephemeral represents a volume that is handled + by a cluster storage driver. The volume's lifecycle is + tied to the pod that defines it - it will be created before + the pod starts, and deleted when the pod is removed. \n + Use this if: a) the volume is only needed while the pod + runs, b) features of normal volumes like restoring from + snapshot or capacity tracking are needed, c) the storage + driver is specified through a storage class, and d) the + storage driver supports dynamic volume provisioning through + \ a PersistentVolumeClaim (see EphemeralVolumeSource + for more information on the connection between this + volume type and PersistentVolumeClaim). \n Use PersistentVolumeClaim + or one of the vendor-specific APIs for volumes that persist + for longer than the lifecycle of an individual pod. \n + Use CSI for light-weight local ephemeral volumes if the + CSI driver is meant to be used that way - see the documentation + of the driver for more information. \n A pod can use both + types of ephemeral volumes and persistent volumes at the + same time." + properties: + volumeClaimTemplate: + description: "Will be used to create a stand-alone PVC + to provision the volume. The pod in which this EphemeralVolumeSource + is embedded will be the owner of the PVC, i.e. the + PVC will be deleted together with the pod. The name + of the PVC will be `-` where + `` is the name from the `PodSpec.Volumes` + array entry. Pod validation will reject the pod if + the concatenated name is not valid for a PVC (for + example, too long). \n An existing PVC with that name + that is not owned by the pod will *not* be used for + the pod to avoid using an unrelated volume by mistake. + Starting the pod is then blocked until the unrelated + PVC is removed. If such a pre-created PVC is meant + to be used by the pod, the PVC has to updated with + an owner reference to the pod once the pod exists. + Normally this should not be necessary, but it may + be useful when manually reconstructing a broken cluster. + \n This field is read-only and no changes will be + made by Kubernetes to the PVC after it has been created. + \n Required, must not be nil." + properties: + metadata: + description: May contain labels and annotations + that will be copied into the PVC when creating + it. No other fields are allowed and will be rejected + during validation. + type: object + spec: + description: The specification for the PersistentVolumeClaim. + The entire content is copied unchanged into the + PVC that gets created from this template. The + same fields as in a PersistentVolumeClaim are + also valid here. + properties: + accessModes: + description: 'accessModes contains the desired + access modes the volume should have. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'dataSource field can be used to + specify either: * An existing VolumeSnapshot + object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) + If the provisioner or an external controller + can support the specified data source, it + will create a new volume based on the contents + of the specified data source. If the AnyVolumeDataSource + feature gate is enabled, this field will always + have the same contents as the DataSourceRef + field.' + properties: + apiGroup: + description: APIGroup is the group for the + resource being referenced. If APIGroup + is not specified, the specified Kind must + be in the core API group. For any other + third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource + being referenced + type: string + name: + description: Name is the name of resource + being referenced + type: string + required: + - kind + - name + type: object + dataSourceRef: + description: 'dataSourceRef specifies the object + from which to populate the volume with data, + if a non-empty volume is desired. This may + be any local object from a non-empty API group + (non core object) or a PersistentVolumeClaim + object. When this field is specified, volume + binding will only succeed if the type of the + specified object matches some installed volume + populator or dynamic provisioner. This field + will replace the functionality of the DataSource + field and as such if both fields are non-empty, + they must have the same value. For backwards + compatibility, both fields (DataSource and + DataSourceRef) will be set to the same value + automatically if one of them is empty and + the other is non-empty. There are two important + differences between DataSource and DataSourceRef: + * While DataSource only allows two specific + types of objects, DataSourceRef allows any + non-core object, as well as PersistentVolumeClaim + objects. * While DataSource ignores disallowed + values (dropping them), DataSourceRef preserves + all values, and generates an error if a disallowed + value is specified. (Beta) Using this field + requires the AnyVolumeDataSource feature gate + to be enabled.' + properties: + apiGroup: + description: APIGroup is the group for the + resource being referenced. If APIGroup + is not specified, the specified Kind must + be in the core API group. For any other + third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource + being referenced + type: string + name: + description: Name is the name of resource + being referenced + type: string + required: + - kind + - name + type: object + resources: + description: 'resources represents the minimum + resources the volume should have. If RecoverVolumeExpansionFailure + feature is enabled users are allowed to specify + resource requirements that are lower than + previous value but must still be higher than + capacity recorded in the status field of the + claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum + amount of compute resources allowed. More + info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum + amount of compute resources required. + If Requests is omitted for a container, + it defaults to Limits if that is explicitly + specified, otherwise to an implementation-defined + value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + selector: + description: selector is a label query over + volumes to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + storageClassName: + description: 'storageClassName is the name of + the StorageClass required by the claim. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type of + volume is required by the claim. Value of + Filesystem is implied when not included in + claim spec. + type: string + volumeName: + description: volumeName is the binding reference + to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: fc represents a Fibre Channel resource that + is attached to a kubelet's host machine and then exposed + to the pod. + properties: + fsType: + description: 'fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. TODO: how do we prevent + errors in the filesystem from compromising the machine' + type: string + lun: + description: 'lun is Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'readOnly is Optional: Defaults to false + (read/write). ReadOnly here will force the ReadOnly + setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC target worldwide + names (WWNs)' + items: + type: string + type: array + wwids: + description: 'wwids Optional: FC volume world wide identifiers + (wwids) Either wwids or combination of targetWWNs + and lun must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: flexVolume represents a generic volume resource + that is provisioned/attached using an exec based plugin. + properties: + driver: + description: driver is the name of the driver to use + for this volume. + type: string + fsType: + description: fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". The default filesystem + depends on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'options is Optional: this field holds + extra command options if any.' + type: object + readOnly: + description: 'readOnly is Optional: defaults to false + (read/write). ReadOnly here will force the ReadOnly + setting in VolumeMounts.' + type: boolean + secretRef: + description: 'secretRef is Optional: secretRef is reference + to the secret object containing sensitive information + to pass to the plugin scripts. This may be empty if + no secret object is specified. If the secret object + contains more than one secret, all secrets are passed + to the plugin scripts.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + required: + - driver + type: object + flocker: + description: flocker represents a Flocker volume attached + to a kubelet's host machine. This depends on the Flocker + control service being running + properties: + datasetName: + description: datasetName is Name of the dataset stored + as metadata -> name on the dataset for Flocker should + be considered as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of the dataset. + This is unique identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'gcePersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then + exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'fsType is filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'partition is the partition in the volume + that you want to mount. If omitted, the default is + to mount by volume name. Examples: For volume /dev/sda1, + you specify the partition as "1". Similarly, the volume + partition for /dev/sda is "0" (or you can leave the + property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'pdName is unique name of the PD resource + in GCE. Used to identify the disk in GCE. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'gitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision + a container with a git repo, mount an EmptyDir into an + InitContainer that clones the repo using git, then mount + the EmptyDir into the Pod''s container.' + properties: + directory: + description: directory is the target directory name. + Must not contain or start with '..'. If '.' is supplied, + the volume directory will be the git repository. Otherwise, + if specified, the volume will contain the git repository + in the subdirectory with the given name. + type: string + repository: + description: repository is the URL + type: string + revision: + description: revision is the commit hash for the specified + revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'glusterfs represents a Glusterfs mount on + the host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'endpoints is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'path is the Glusterfs volume path. More + info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'readOnly here will force the Glusterfs + volume to be mounted with read-only permissions. Defaults + to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'hostPath represents a pre-existing file or + directory on the host machine that is directly exposed + to the container. This is generally used for system agents + or other privileged things that are allowed to see the + host machine. Most containers will NOT need this. More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host + directory mounts and who can/can not mount host directories + as read/write.' + properties: + path: + description: 'path of the directory on the host. If + the path is a symlink, it will follow the link to + the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'type for HostPath Volume Defaults to "" + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'iscsi represents an ISCSI Disk resource that + is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether support + iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether support + iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + initiatorName: + description: initiatorName is the custom iSCSI Initiator + Name. If initiatorName is specified with iscsiInterface + simultaneously, new iSCSI interface : will be created for the connection. + type: string + iqn: + description: iqn is the target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iscsiInterface is the interface Name that + uses an iSCSI transport. Defaults to 'default' (tcp). + type: string + lun: + description: lun represents iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: portals is the iSCSI Target Portal List. + The portal is either an IP or ip_addr:port if the + port is other than default (typically TCP ports 860 + and 3260). + items: + type: string + type: array + readOnly: + description: readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret for iSCSI + target and initiator authentication + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + targetPortal: + description: targetPortal is iSCSI Target Portal. The + Portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and + 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'name of the volume. Must be a DNS_LABEL and + unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'nfs represents an NFS mount on the host that + shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'path that is exported by the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'readOnly here will force the NFS export + to be mounted with read-only permissions. Defaults + to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'server is the hostname or IP address of + the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'persistentVolumeClaimVolumeSource represents + a reference to a PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'claimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: readOnly Will force the ReadOnly setting + in VolumeMounts. Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: photonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host + machine + properties: + fsType: + description: fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + pdID: + description: pdID is the ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: portworxVolume represents a portworx volume + attached and mounted on kubelets host machine + properties: + fsType: + description: fSType represents the filesystem type to + mount Must be a filesystem type supported by the host + operating system. Ex. "ext4", "xfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies a Portworx + volume + type: string + required: + - volumeID + type: object + projected: + description: projected items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: defaultMode are the mode bits used to set + permissions on created files by default. Must be an + octal value between 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts both octal and decimal + values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this + setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set. + format: int32 + type: integer + sources: + description: sources is the list of volume projections + items: + description: Projection that may be projected along + with other supported volume types + properties: + configMap: + description: configMap information about the configMap + data to project + properties: + items: + description: items if unspecified, each key-value + pair in the Data field of the referenced + ConfigMap will be projected into the volume + as a file whose name is the key and content + is the value. If specified, the listed keys + will be projected into the specified paths, + and unlisted keys will not be present. If + a key is specified which is not present + in the ConfigMap, the volume setup will + error unless it is marked optional. Paths + must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path + within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode + bits used to set permissions on this + file. Must be an octal value between + 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal + and decimal values, JSON requires + decimal values for mode bits. If not + specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the + file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: path is the relative path + of the file to map the key to. May + not be an absolute path. May not contain + the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: optional specify whether the + ConfigMap or its keys must be defined + type: boolean + type: object + downwardAPI: + description: downwardAPI information about the + downwardAPI data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name and namespace are supported.' + properties: + apiVersion: + description: Version of the schema + the FieldPath is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to + select in the specified API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode bits used + to set permissions on this file, must + be an octal value between 0000 and + 0777 or a decimal value between 0 + and 511. YAML accepts both octal and + decimal values, JSON requires decimal + values for mode bits. If not specified, + the volume defaultMode will be used. + This might be in conflict with other + options that affect the file mode, + like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the + relative path name of the file to + be created. Must not be absolute or + contain the ''..'' path. Must be utf-8 + encoded. The first item of the relative + path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of + the container: only resources limits + and requests (limits.cpu, limits.memory, + requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env + vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output + format of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource + to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: secret information about the secret + data to project + properties: + items: + description: items if unspecified, each key-value + pair in the Data field of the referenced + Secret will be projected into the volume + as a file whose name is the key and content + is the value. If specified, the listed keys + will be projected into the specified paths, + and unlisted keys will not be present. If + a key is specified which is not present + in the Secret, the volume setup will error + unless it is marked optional. Paths must + be relative and may not contain the '..' + path or start with '..'. + items: + description: Maps a string key to a path + within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode + bits used to set permissions on this + file. Must be an octal value between + 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal + and decimal values, JSON requires + decimal values for mode bits. If not + specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the + file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: path is the relative path + of the file to map the key to. May + not be an absolute path. May not contain + the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: optional field specify whether + the Secret or its key must be defined + type: boolean + type: object + serviceAccountToken: + description: serviceAccountToken is information + about the serviceAccountToken data to project + properties: + audience: + description: audience is the intended audience + of the token. A recipient of a token must + identify itself with an identifier specified + in the audience of the token, and otherwise + should reject the token. The audience defaults + to the identifier of the apiserver. + type: string + expirationSeconds: + description: expirationSeconds is the requested + duration of validity of the service account + token. As the token approaches expiration, + the kubelet volume plugin will proactively + rotate the service account token. The kubelet + will start trying to rotate the token if + the token is older than 80 percent of its + time to live or if the token is older than + 24 hours.Defaults to 1 hour and must be + at least 10 minutes. + format: int64 + type: integer + path: + description: path is the path relative to + the mount point of the file to project the + token into. + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + description: quobyte represents a Quobyte mount on the host + that shares a pod's lifetime + properties: + group: + description: group to map volume access to Default is + no group + type: string + readOnly: + description: readOnly here will force the Quobyte volume + to be mounted with read-only permissions. Defaults + to false. + type: boolean + registry: + description: registry represents a single or multiple + Quobyte Registry services specified as a string as + host:port pair (multiple entries are separated with + commas) which acts as the central registry for volumes + type: string + tenant: + description: tenant owning the given Quobyte volume + in the Backend Used with dynamically provisioned Quobyte + volumes, value is set by the plugin + type: string + user: + description: user to map volume access to Defaults to + serivceaccount user + type: string + volume: + description: volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'rbd represents a Rados Block Device mount + on the host that shares a pod''s lifetime. More info: + https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + image: + description: 'image is the rados image name. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'pool is the rados pool name. Default is + rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'secretRef is name of the authentication + secret for RBDUser. If provided overrides keyring. + Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + user: + description: 'user is the rados user name. Default is + admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: scaleIO represents a ScaleIO persistent volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: gateway is the host address of the ScaleIO + API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name of the ScaleIO + Protection Domain for the configured storage. + type: string + readOnly: + description: readOnly Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef references to the secret for + ScaleIO user and other sensitive information. If this + is not provided, Login operation will fail. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + sslEnabled: + description: sslEnabled Flag enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + description: storageMode indicates whether the storage + for a volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO Storage Pool + associated with the protection domain. + type: string + system: + description: system is the name of the storage system + as configured in ScaleIO. + type: string + volumeName: + description: volumeName is the name of a volume already + created in the ScaleIO system that is associated with + this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'defaultMode is Optional: mode bits used + to set permissions on created files by default. Must + be an octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both octal and + decimal values, JSON requires decimal values for mode + bits. Defaults to 0644. Directories within the path + are not affected by this setting. This might be in + conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + items: + description: items If unspecified, each key-value pair + in the Data field of the referenced Secret will be + projected into the volume as a file whose name is + the key and content is the value. If specified, the + listed keys will be projected into the specified paths, + and unlisted keys will not be present. If a key is + specified which is not present in the Secret, the + volume setup will error unless it is marked optional. + Paths must be relative and may not contain the '..' + path or start with '..'. + items: + description: Maps a string key to a path within a + volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used + to set permissions on this file. Must be an + octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both octal + and decimal values, JSON requires decimal values + for mode bits. If not specified, the volume + defaultMode will be used. This might be in conflict + with other options that affect the file mode, + like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: path is the relative path of the + file to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: optional field specify whether the Secret + or its keys must be defined + type: boolean + secretName: + description: 'secretName is the name of the secret in + the pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: storageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef specifies the secret to use for + obtaining the StorageOS API credentials. If not specified, + default values will be attempted. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + volumeName: + description: volumeName is the human-readable name of + the StorageOS volume. Volume names are only unique + within a namespace. + type: string + volumeNamespace: + description: volumeNamespace specifies the scope of + the volume within StorageOS. If no namespace is specified + then the Pod's namespace will be used. This allows + the Kubernetes name scoping to be mirrored within + StorageOS for tighter integration. Set VolumeName + to any name to override the default behaviour. Set + to "default" if you are not using namespaces within + StorageOS. Namespaces that do not pre-exist within + StorageOS will be created. + type: string + type: object + vsphereVolume: + description: vsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: fsType is filesystem type to mount. Must + be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage Policy Based + Management (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage Policy + Based Management (SPBM) profile name. + type: string + volumePath: + description: volumePath is the path that identifies + vSphere volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array required: - serviceAccountName type: object @@ -1311,6 +4228,47 @@ spec: startTime: description: (Optional) Test Start time. type: string + volumeMounts: + description: (Optional) Pod volumes to mount into the container's + filesystem. + items: + description: VolumeMount describes a mounting of a Volume within + a container. + properties: + mountPath: + description: Path within the container at which the volume + should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are + propagated from the host to container and the other way + around. When not set, MountPropagationNone is used. This + field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which + the container's volume should be mounted. Behaves similarly + to SubPath but environment variable references $(VAR_NAME) + are expanded using the container's environment. Defaults + to "" (volume's root). SubPathExpr and SubPath are mutually + exclusive. + type: string + required: + - mountPath + - name + type: object + type: array required: - simulationClass type: object diff --git a/controllers/gatling_controller.go b/controllers/gatling_controller.go index 07c302d..f25b2be 100644 --- a/controllers/gatling_controller.go +++ b/controllers/gatling_controller.go @@ -134,53 +134,8 @@ func (r *GatlingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct // Implementation of reconciler logic for the runner job func (r *GatlingReconciler) gatlingRunnerReconcile(ctx context.Context, req ctrl.Request, gatling *gatlingv1alpha1.Gatling, log logr.Logger) (bool, error) { - // Create Simulation Data ConfigMap if defined to create in CR - if &gatling.Spec.TestScenarioSpec.SimulationData != nil && len(gatling.Spec.TestScenarioSpec.SimulationData) > 0 { - configMapName := gatling.Name + "-simulations-data" - foundConfigMap := &corev1.ConfigMap{} - if err := r.Get(ctx, client.ObjectKey{Name: configMapName, Namespace: req.Namespace}, foundConfigMap); err != nil { - if apierr.IsNotFound(err) { - simulationDataConfigMap := r.newConfigMapForCR(gatling, configMapName, &gatling.Spec.TestScenarioSpec.SimulationData) - if err := r.createObject(ctx, gatling, simulationDataConfigMap); err != nil { - log.Error(err, fmt.Sprintf("Failed to creating new ConfigMap: namespace %s name %s", simulationDataConfigMap.GetNamespace(), simulationDataConfigMap.GetName())) - return true, err - } - } else { - return true, err - } - } - } - // Create Resource Data ConfigMap if defined to create in CR - if &gatling.Spec.TestScenarioSpec.ResourceData != nil && len(gatling.Spec.TestScenarioSpec.ResourceData) > 0 { - configMapName := gatling.Name + "-resources-data" - foundConfigMap := &corev1.ConfigMap{} - if err := r.Get(ctx, client.ObjectKey{Name: configMapName, Namespace: req.Namespace}, foundConfigMap); err != nil { - if apierr.IsNotFound(err) { - resourceDataConfigMap := r.newConfigMapForCR(gatling, configMapName, &gatling.Spec.TestScenarioSpec.ResourceData) - if err := r.createObject(ctx, gatling, resourceDataConfigMap); err != nil { - log.Error(err, fmt.Sprintf("Failed to creating new ConfigMap: namespace %s name %s", resourceDataConfigMap.GetNamespace(), resourceDataConfigMap.GetName())) - return true, err - } - } else { - return true, err - } - } - } - // Create GatlingConf ConfigMap if defined to create in CR - if &gatling.Spec.TestScenarioSpec.GatlingConf != nil && len(gatling.Spec.TestScenarioSpec.GatlingConf) > 0 { - configMapName := gatling.Name + "-gatling-conf" - foundConfigMap := &corev1.ConfigMap{} - if err := r.Get(ctx, client.ObjectKey{Name: configMapName, Namespace: req.Namespace}, foundConfigMap); err != nil { - if apierr.IsNotFound(err) { - gatlingConfConfigMap := r.newConfigMapForCR(gatling, configMapName, &gatling.Spec.TestScenarioSpec.GatlingConf) - if err := r.createObject(ctx, gatling, gatlingConfConfigMap); err != nil { - log.Error(err, fmt.Sprintf("Failed to creating new ConfigMap: namespace %s name %s", gatlingConfConfigMap.GetNamespace(), gatlingConfConfigMap.GetName())) - return true, err - } - } else { - return true, err - } - } + if err := r.createObjectsForCR(ctx, gatling, req.Namespace, log); err != nil { + return true, err } if gatling.Status.RunnerJobName == "" { var storagePath = "" @@ -406,6 +361,83 @@ func doNotRequeue(err error) (ctrl.Result, error) { return ctrl.Result{}, err } +func (r *GatlingReconciler) createObjectsForCR(ctx context.Context, gatling *gatlingv1alpha1.Gatling, namespace string, log logr.Logger) error { + // Create Simulation Data ConfigMap if defined to create in CR + if &gatling.Spec.TestScenarioSpec.SimulationData != nil && len(gatling.Spec.TestScenarioSpec.SimulationData) > 0 { + configMapName := gatling.Name + "-simulations-data" + foundConfigMap := &corev1.ConfigMap{} + if err := r.Get(ctx, client.ObjectKey{Name: configMapName, Namespace: namespace}, foundConfigMap); err != nil { + if !apierr.IsNotFound(err) { + return err + } + simulationDataConfigMap := r.newConfigMapForCR(gatling, configMapName, &gatling.Spec.TestScenarioSpec.SimulationData) + if err := r.createObject(ctx, gatling, simulationDataConfigMap); err != nil { + log.Error(err, fmt.Sprintf("Failed to creating new ConfigMap: namespace %s name %s", simulationDataConfigMap.GetNamespace(), simulationDataConfigMap.GetName())) + return err + } + } + } + // Create Resource Data ConfigMap if defined to create in CR + if &gatling.Spec.TestScenarioSpec.ResourceData != nil && len(gatling.Spec.TestScenarioSpec.ResourceData) > 0 { + configMapName := gatling.Name + "-resources-data" + foundConfigMap := &corev1.ConfigMap{} + if err := r.Get(ctx, client.ObjectKey{Name: configMapName, Namespace: namespace}, foundConfigMap); err != nil { + if !apierr.IsNotFound(err) { + return err + } + resourceDataConfigMap := r.newConfigMapForCR(gatling, configMapName, &gatling.Spec.TestScenarioSpec.ResourceData) + if err := r.createObject(ctx, gatling, resourceDataConfigMap); err != nil { + log.Error(err, fmt.Sprintf("Failed to creating new ConfigMap: namespace %s name %s", resourceDataConfigMap.GetNamespace(), resourceDataConfigMap.GetName())) + return err + } + } + } + // Create GatlingConf ConfigMap if defined to create in CR + if &gatling.Spec.TestScenarioSpec.GatlingConf != nil && len(gatling.Spec.TestScenarioSpec.GatlingConf) > 0 { + configMapName := gatling.Name + "-gatling-conf" + foundConfigMap := &corev1.ConfigMap{} + if err := r.Get(ctx, client.ObjectKey{Name: configMapName, Namespace: namespace}, foundConfigMap); err != nil { + if !apierr.IsNotFound(err) { + return err + } + gatlingConfConfigMap := r.newConfigMapForCR(gatling, configMapName, &gatling.Spec.TestScenarioSpec.GatlingConf) + if err := r.createObject(ctx, gatling, gatlingConfConfigMap); err != nil { + log.Error(err, fmt.Sprintf("Failed to creating new ConfigMap: namespace %s name %s", gatlingConfConfigMap.GetNamespace(), gatlingConfConfigMap.GetName())) + return err + } + } + } + // Create PersistentVolume if defined to create in CR + if &gatling.Spec.PersistentVolume != nil && gatling.Spec.PersistentVolume.GetName() != "" { + volumeName := gatling.Spec.PersistentVolume.GetName() + foundVolue := &corev1.PersistentVolume{} + if err := r.Get(ctx, client.ObjectKey{Name: volumeName, Namespace: namespace}, foundVolue); err != nil { + if !apierr.IsNotFound(err) { + return err + } + if err := r.createObject(ctx, gatling, &gatling.Spec.PersistentVolume); err != nil { + log.Error(err, fmt.Sprintf("Failed to creating new PersistentVolume: namespace %s name %s", gatling.Spec.PersistentVolume.GetNamespace(), gatling.Spec.PersistentVolume.GetName())) + return err + } + } + } + // Create PersistentVolumeClaim if defined to create in CR + if &gatling.Spec.PersistentVolumeClaim != nil && gatling.Spec.PersistentVolumeClaim.GetName() != "" { + claimName := gatling.Spec.PersistentVolumeClaim.GetName() + foundClaim := &corev1.PersistentVolumeClaim{} + if err := r.Get(ctx, client.ObjectKey{Name: claimName, Namespace: namespace}, foundClaim); err != nil { + if !apierr.IsNotFound(err) { + return err + } + if err := r.createObject(ctx, gatling, &gatling.Spec.PersistentVolumeClaim); err != nil { + log.Error(err, fmt.Sprintf("Failed to creating new PersistentVolumeClaim: namespace %s name %s", gatling.Spec.PersistentVolumeClaim.GetNamespace(), gatling.Spec.PersistentVolumeClaim.GetName())) + return err + } + } + } + return nil +} + func (r *GatlingReconciler) newConfigMapForCR(gatling *gatlingv1alpha1.Gatling, configMapName string, configMapData *map[string]string) *corev1.ConfigMap { labels := map[string]string{ "app": gatling.Name, @@ -464,8 +496,8 @@ func (r *GatlingReconciler) newGatlingRunnerJobForCR(gatling *gatlingv1alpha1.Ga }, Spec: batchv1.JobSpec{ BackoffLimit: &noRestarts, - Parallelism: r.getGatlingRunnerJobParallelism(gatling), - Completions: r.getGatlingRunnerJobParallelism(gatling), + Parallelism: r.getGatlingRunnerJobParallelism(gatling), + Completions: r.getGatlingRunnerJobParallelism(gatling), Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Name: r.getObjectMeta(gatling).Name, @@ -531,8 +563,8 @@ func (r *GatlingReconciler) newGatlingRunnerJobForCR(gatling *gatlingv1alpha1.Ga }, Spec: batchv1.JobSpec{ BackoffLimit: &noRestarts, - Parallelism: &gatling.Spec.TestScenarioSpec.Parallelism, - Completions: &gatling.Spec.TestScenarioSpec.Parallelism, + Parallelism: &gatling.Spec.TestScenarioSpec.Parallelism, + Completions: &gatling.Spec.TestScenarioSpec.Parallelism, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Name: r.getObjectMeta(gatling).Name, @@ -695,10 +727,13 @@ func (r *GatlingReconciler) getGatlingRunnerJobVolumeMounts(gatling *gatlingv1al MountPath: r.getResultsDirectoryPath(gatling), }) } + if gatling.Spec.TestScenarioSpec.VolumeMounts != nil && len(gatling.Spec.TestScenarioSpec.VolumeMounts) > 0 { + volumeMounts = append(volumeMounts, gatling.Spec.TestScenarioSpec.VolumeMounts...) + } return volumeMounts } -// ConfigMap Volume Source +// Volume Source func (r *GatlingReconciler) getGatlingRunnerJobVolumes(gatling *gatlingv1alpha1.Gatling) []corev1.Volume { volumes := make([]corev1.Volume, 0) if &gatling.Spec.TestScenarioSpec.SimulationData != nil && len(gatling.Spec.TestScenarioSpec.SimulationData) > 0 { @@ -734,6 +769,10 @@ func (r *GatlingReconciler) getGatlingRunnerJobVolumes(gatling *gatlingv1alpha1. }) } + if gatling.Spec.PodSpec.Volumes != nil && len(gatling.Spec.PodSpec.Volumes) > 0 { + volumes = append(volumes, gatling.Spec.PodSpec.Volumes...) + } + volumes = append(volumes, corev1.Volume{ Name: "pod-info", VolumeSource: corev1.VolumeSource{ From cf2819897b7a69e1a1d3e4d08daebc6c81b1d03d Mon Sep 17 00:00:00 2001 From: kane8n Date: Tue, 13 Feb 2024 20:30:55 +0900 Subject: [PATCH 2/8] bug fix --- api/v1alpha1/gatling_types.go | 24 ++- api/v1alpha1/zz_generated.deepcopy.go | 36 +++- ...tling-operator.tech.zozo.com_gatlings.yaml | 157 ++---------------- config/rbac/leader_election_role.yaml | 2 + config/rbac/role.yaml | 24 +++ controllers/gatling_controller.go | 69 +++++++- 6 files changed, 157 insertions(+), 155 deletions(-) diff --git a/api/v1alpha1/gatling_types.go b/api/v1alpha1/gatling_types.go index 5829937..9dcbfc1 100644 --- a/api/v1alpha1/gatling_types.go +++ b/api/v1alpha1/gatling_types.go @@ -56,11 +56,11 @@ type GatlingSpec struct { // (Optional) PersistentVolume specification. // +kubebuilder:validation:Optional - PersistentVolume corev1.PersistentVolume `json:"persistentVolume,omitempty"` + PersistentVolumeSpec PersistentVolumeSpec `json:"persistentVolume,omitempty"` // (Optional) PersistentVolumeClaim specification. // +kubebuilder:validation:Optional - PersistentVolumeClaim corev1.PersistentVolumeClaim `json:"persistentVolumeClaim,omitempty"` + PersistentVolumeClaimSpec PersistentVolumeClaimSpec `json:"persistentVolumeClaim,omitempty"` // (Optional) Notification Service specification. // +kubebuilder:validation:Optional @@ -183,6 +183,26 @@ type NotificationServiceSpec struct { SecretName string `json:"secretName"` } +type PersistentVolumeSpec struct { + // (Required) The name of the PersistentVolume. + // +kubebuilder:validation:Required + Name string `json:"name"` + + // (Required) PersistentVolumeSpec is the specification of a persistent volume. + // +kubebuilder:validation:Required + Spec corev1.PersistentVolumeSpec `json:"spec"` +} + +type PersistentVolumeClaimSpec struct { + // (Required) The name of the PersistentVolumeClaim. + // +kubebuilder:validation:Required + Name string `json:"name"` + + // (Required) PersistentVolumeClaimSpec is the specification of a persistent volume. + // +kubebuilder:validation:Required + Spec corev1.PersistentVolumeClaimSpec `json:"spec"` +} + // GatlingStatus defines the observed state of Gatling type GatlingStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 35c5643..b37d417 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -112,8 +112,8 @@ func (in *GatlingSpec) DeepCopyInto(out *GatlingSpec) { *out = *in in.PodSpec.DeepCopyInto(&out.PodSpec) in.CloudStorageSpec.DeepCopyInto(&out.CloudStorageSpec) - in.PersistentVolume.DeepCopyInto(&out.PersistentVolume) - in.PersistentVolumeClaim.DeepCopyInto(&out.PersistentVolumeClaim) + in.PersistentVolumeSpec.DeepCopyInto(&out.PersistentVolumeSpec) + in.PersistentVolumeClaimSpec.DeepCopyInto(&out.PersistentVolumeClaimSpec) out.NotificationServiceSpec = in.NotificationServiceSpec in.TestScenarioSpec.DeepCopyInto(&out.TestScenarioSpec) } @@ -158,6 +158,38 @@ func (in *NotificationServiceSpec) DeepCopy() *NotificationServiceSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PersistentVolumeClaimSpec) DeepCopyInto(out *PersistentVolumeClaimSpec) { + *out = *in + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PersistentVolumeClaimSpec. +func (in *PersistentVolumeClaimSpec) DeepCopy() *PersistentVolumeClaimSpec { + if in == nil { + return nil + } + out := new(PersistentVolumeClaimSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PersistentVolumeSpec) DeepCopyInto(out *PersistentVolumeSpec) { + *out = *in + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PersistentVolumeSpec. +func (in *PersistentVolumeSpec) DeepCopy() *PersistentVolumeSpec { + if in == nil { + return nil + } + out := new(PersistentVolumeSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PodSpec) DeepCopyInto(out *PodSpec) { *out = *in diff --git a/config/crd/bases/gatling-operator.tech.zozo.com_gatlings.yaml b/config/crd/bases/gatling-operator.tech.zozo.com_gatlings.yaml index 2d17408..5201164 100644 --- a/config/crd/bases/gatling-operator.tech.zozo.com_gatlings.yaml +++ b/config/crd/bases/gatling-operator.tech.zozo.com_gatlings.yaml @@ -218,25 +218,12 @@ spec: persistentVolume: description: (Optional) PersistentVolume specification. 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' + name: + description: (Required) The name of the PersistentVolume. 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: - description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' - type: object spec: - description: 'spec defines a specification of a persistent volume - owned by the cluster. Provisioned by an administrator. More - info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistent-volumes' + description: (Required) PersistentVolumeSpec is the specification + of a persistent volume. properties: accessModes: description: 'accessModes contains all ways the volume can @@ -1281,47 +1268,19 @@ spec: - volumePath type: object type: object - status: - description: 'status represents the current information/status - for the persistent volume. Populated by the system. Read-only. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistent-volumes' - properties: - message: - description: message is a human-readable message indicating - details about why the volume is in this state. - type: string - phase: - description: 'phase indicates if a volume is available, bound - to a claim, or released by a claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#phase' - type: string - reason: - description: reason is a brief CamelCase string that describes - any failure and is meant for machine parsing and tidy display - in the CLI. - type: string - type: object + required: + - name + - spec type: object persistentVolumeClaim: description: (Optional) PersistentVolumeClaim specification. 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' + name: + description: (Required) The name of the PersistentVolumeClaim. type: string - metadata: - description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' - type: object spec: - description: 'spec defines the desired characteristics of a volume - requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + description: (Required) PersistentVolumeClaimSpec is the specification + of a persistent volume. properties: accessModes: description: 'accessModes contains the desired access modes @@ -1485,97 +1444,9 @@ spec: backing this claim. type: string type: object - status: - description: 'status represents the current information/status - of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - properties: - accessModes: - description: 'accessModes contains the actual access modes - the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' - items: - type: string - type: array - allocatedResources: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: allocatedResources is the storage resource within - AllocatedResources tracks the capacity allocated to a PVC. - It may be larger than the actual capacity when a volume - expansion operation is requested. For storage quota, the - larger value from allocatedResources and PVC.spec.resources - is used. If allocatedResources is not set, PVC.spec.resources - alone is used for quota calculation. If a volume expansion - capacity request is lowered, allocatedResources is only - lowered if there are no expansion operations in progress - and if the actual volume capacity is equal or lower than - the requested capacity. This is an alpha field and requires - enabling RecoverVolumeExpansionFailure feature. - type: object - capacity: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: capacity represents the actual resources of the - underlying volume. - type: object - conditions: - description: conditions is the current Condition of persistent - volume claim. If underlying persistent volume is being resized - then the Condition will be set to 'ResizeStarted'. - items: - description: PersistentVolumeClaimCondition contails details - about state of pvc - properties: - lastProbeTime: - description: lastProbeTime is the time we probed the - condition. - format: date-time - type: string - lastTransitionTime: - description: lastTransitionTime is the time the condition - transitioned from one status to another. - format: date-time - type: string - message: - description: message is the human-readable message indicating - details about last transition. - type: string - reason: - description: reason is a unique, this should be a short, - machine understandable string that gives the reason - for condition's last transition. If it reports "ResizeStarted" - that means the underlying persistent volume is being - resized. - type: string - status: - type: string - type: - description: PersistentVolumeClaimConditionType is a - valid value of PersistentVolumeClaimCondition.Type - type: string - required: - - status - - type - type: object - type: array - phase: - description: phase represents the current phase of PersistentVolumeClaim. - type: string - resizeStatus: - description: resizeStatus stores status of resize operation. - ResizeStatus is not set by default but when expansion is - complete resizeStatus is set to empty string by resize controller - or kubelet. This is an alpha field and requires enabling - RecoverVolumeExpansionFailure feature. - type: string - type: object + required: + - name + - spec type: object podSpec: description: (Optional) Gatling Pod specification. diff --git a/config/rbac/leader_election_role.yaml b/config/rbac/leader_election_role.yaml index 4190ec8..9e40ee1 100644 --- a/config/rbac/leader_election_role.yaml +++ b/config/rbac/leader_election_role.yaml @@ -8,6 +8,8 @@ rules: - "" resources: - configmaps + - persistentvolumes + - persistentvolumeclaims verbs: - get - list diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 989a909..3f517a1 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -30,6 +30,30 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - "" resources: diff --git a/controllers/gatling_controller.go b/controllers/gatling_controller.go index f25b2be..35463ce 100644 --- a/controllers/gatling_controller.go +++ b/controllers/gatling_controller.go @@ -64,6 +64,8 @@ type GatlingReconciler struct { //+kubebuilder:rbac:groups="batch",resources=jobs,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups="core",resources=configmaps,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups="core",resources=persistentvolumes,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups="core",resources=persistentvolumeclaims,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups="core",resources=secrets,verbs=get;list;watch //+kubebuilder:rbac:groups=gatling-operator.tech.zozo.com,resources=gatlings,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=gatling-operator.tech.zozo.com,resources=gatlings/status,verbs=get;update;patch @@ -87,6 +89,13 @@ func (r *GatlingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct if gatling.Spec.CleanupAfterJobDone { log.Info(fmt.Sprintf("Cleaning up gatlig %s", gatling.Name)) r.cleanupGatling(ctx, req, gatling.Name) + if gatling.Spec.PersistentVolumeSpec.Name != "" { + log.Info(fmt.Sprintf("Cleaning up persistent volume %s", gatling.Spec.PersistentVolumeSpec.Name)) + if err := r.cleanupPersistentVolume(ctx, req, gatling.Spec.PersistentVolumeSpec.Name); err != nil { + log.Error(err, "Failed to clean up persistent volume") + return doNotRequeue(err) + } + } } return doNotRequeue(nil) } @@ -408,29 +417,31 @@ func (r *GatlingReconciler) createObjectsForCR(ctx context.Context, gatling *gat } } // Create PersistentVolume if defined to create in CR - if &gatling.Spec.PersistentVolume != nil && gatling.Spec.PersistentVolume.GetName() != "" { - volumeName := gatling.Spec.PersistentVolume.GetName() + if &gatling.Spec.PersistentVolumeSpec != nil && gatling.Spec.PersistentVolumeSpec.Name != "" { + volumeName := gatling.Spec.PersistentVolumeSpec.Name foundVolue := &corev1.PersistentVolume{} if err := r.Get(ctx, client.ObjectKey{Name: volumeName, Namespace: namespace}, foundVolue); err != nil { if !apierr.IsNotFound(err) { return err } - if err := r.createObject(ctx, gatling, &gatling.Spec.PersistentVolume); err != nil { - log.Error(err, fmt.Sprintf("Failed to creating new PersistentVolume: namespace %s name %s", gatling.Spec.PersistentVolume.GetNamespace(), gatling.Spec.PersistentVolume.GetName())) + persistentVolume := r.newPersistentVolumeForCR(gatling, volumeName, &gatling.Spec.PersistentVolumeSpec.Spec) + if err := r.createObject(ctx, gatling, persistentVolume); err != nil { + log.Error(err, fmt.Sprintf("Failed to creating new PersistentVolume: namespace %s name %s", gatling.GetNamespace(), volumeName)) return err } } } // Create PersistentVolumeClaim if defined to create in CR - if &gatling.Spec.PersistentVolumeClaim != nil && gatling.Spec.PersistentVolumeClaim.GetName() != "" { - claimName := gatling.Spec.PersistentVolumeClaim.GetName() + if &gatling.Spec.PersistentVolumeClaimSpec != nil && gatling.Spec.PersistentVolumeClaimSpec.Name != "" { + claimName := gatling.Spec.PersistentVolumeClaimSpec.Name foundClaim := &corev1.PersistentVolumeClaim{} if err := r.Get(ctx, client.ObjectKey{Name: claimName, Namespace: namespace}, foundClaim); err != nil { if !apierr.IsNotFound(err) { return err } - if err := r.createObject(ctx, gatling, &gatling.Spec.PersistentVolumeClaim); err != nil { - log.Error(err, fmt.Sprintf("Failed to creating new PersistentVolumeClaim: namespace %s name %s", gatling.Spec.PersistentVolumeClaim.GetNamespace(), gatling.Spec.PersistentVolumeClaim.GetName())) + persistentVolumeClaim := r.newPersistentVolumeClaimForCR(gatling, claimName, &gatling.Spec.PersistentVolumeClaimSpec.Spec) + if err := r.createObject(ctx, gatling, persistentVolumeClaim); err != nil { + log.Error(err, fmt.Sprintf("Failed to creating new PersistentVolumeClaim: namespace %s name %s", gatling.GetNamespace(), claimName)) return err } } @@ -456,6 +467,48 @@ func (r *GatlingReconciler) newConfigMapForCR(gatling *gatlingv1alpha1.Gatling, } } +func (r *GatlingReconciler) newPersistentVolumeForCR(gatling *gatlingv1alpha1.Gatling, name string, spec *corev1.PersistentVolumeSpec) *corev1.PersistentVolume { + labels := map[string]string{ + "app": gatling.Name, + } + return &corev1.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: gatling.Namespace, + Labels: labels, + }, + Spec: *spec, + } +} + +func (r *GatlingReconciler) cleanupPersistentVolume(ctx context.Context, req ctrl.Request, name string) error { + pv := &corev1.PersistentVolume{} + if err := r.Get(ctx, client.ObjectKey{Name: name, Namespace: req.Namespace}, pv); err != nil { + if apierr.IsNotFound(err) { + return nil + } + return err + } + if err := r.Delete(ctx, pv); err != nil { + return err + } + return nil +} + +func (r *GatlingReconciler) newPersistentVolumeClaimForCR(gatling *gatlingv1alpha1.Gatling, name string, spec *corev1.PersistentVolumeClaimSpec) *corev1.PersistentVolumeClaim { + labels := map[string]string{ + "app": gatling.Name, + } + return &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: gatling.Namespace, + Labels: labels, + }, + Spec: *spec, + } +} + func (r *GatlingReconciler) newGatlingRunnerJobForCR(gatling *gatlingv1alpha1.Gatling, storagePath string, log logr.Logger) *batchv1.Job { labels := map[string]string{ "app": gatling.Name, From 0d837fe83db83b3a30190544f18606d6f1826ce9 Mon Sep 17 00:00:00 2001 From: kane8n Date: Wed, 14 Feb 2024 15:53:38 +0900 Subject: [PATCH 3/8] update docs/api.md --- docs/api.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/docs/api.md b/docs/api.md index 8717ccb..c511b5d 100644 --- a/docs/api.md +++ b/docs/api.md @@ -63,6 +63,8 @@ _Appears in:_ | `cleanupAfterJobDone` _boolean_ | (Optional) The flag of cleanup gatling resources after the job done. Defaults to `false` | | `podSpec` _[PodSpec](#podspec)_ | (Optional) Gatling Pod specification. | | `cloudStorageSpec` _[CloudStorageSpec](#cloudstoragespec)_ | (Optional) Cloud Storage Provider specification. | +| `persistentVolume` _[PersistentVolumeSpec](#persistentvolumespec)_ | (Optional) PersistentVolume specification. | +| `persistentVolumeClaim` _[PersistentVolumeClaimSpec](#persistentvolumeclaimspec)_ | (Optional) PersistentVolumeClaim specification. | | `notificationServiceSpec` _[NotificationServiceSpec](#notificationservicespec)_ | (Optional) Notification Service specification. | | `testScenarioSpec` _[TestScenarioSpec](#testscenariospec)_ | (Required) Test Scenario specification | @@ -84,6 +86,36 @@ _Appears in:_ | `secretName` _string_ | (Required) The name of secret in which all key/value sets needed for the notification are stored. | +#### PersistentVolumeClaimSpec + + + + + +_Appears in:_ +- [GatlingSpec](#gatlingspec) + +| Field | Description | +| --- | --- | +| `name` _string_ | (Required) The name of the PersistentVolumeClaim. | +| `spec` _[PersistentVolumeClaimSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#persistentvolumeclaimspec-v1-core)_ | (Required) PersistentVolumeClaimSpec is the specification of a persistent volume. | + + +#### PersistentVolumeSpec + + + + + +_Appears in:_ +- [GatlingSpec](#gatlingspec) + +| Field | Description | +| --- | --- | +| `name` _string_ | (Required) The name of the PersistentVolume. | +| `spec` _[PersistentVolumeSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#persistentvolumespec-v1-core)_ | (Required) PersistentVolumeSpec is the specification of a persistent volume. | + + #### PodSpec @@ -101,6 +133,7 @@ _Appears in:_ | `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#affinity-v1-core)_ | (Optional) Affinity specification. | | `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#toleration-v1-core) array_ | (Optional) Tolerations specification. | | `serviceAccountName` _string_ | (Required) ServiceAccountName specification. | +| `volumes` _[Volume](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#volume-v1-core) array_ | (Optional) volumes specification. | #### TestScenarioSpec @@ -124,5 +157,6 @@ _Appears in:_ | `resourceData` _object (keys:string, values:string)_ | (Optional) Resource Data. | | `gatlingConf` _object (keys:string, values:string)_ | (Optional) Gatling Configurations. | | `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#envvar-v1-core) array_ | (Optional) Environment variables used for running load testing scenario. | +| `volumeMounts` _[VolumeMount](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#volumemount-v1-core) array_ | (Optional) Pod volumes to mount into the container's filesystem. | From 9db5543fb48c772e2a8da88663f7f404367fcbb9 Mon Sep 17 00:00:00 2001 From: kane8n Date: Fri, 1 Mar 2024 18:13:50 +0900 Subject: [PATCH 4/8] add sample --- .../gatling-operator_v1alpha1_gatling03.yaml | 134 ++++++++++++++++++ gatling/sample/resources/goods_ids.csv | 6 + gatling/sample/resources/user_ids.csv | 6 + gatling/user-files/resources/myresources.csv | 1 + .../PersistentVolumeSampleSimulation.scala | 49 +++++++ 5 files changed, 196 insertions(+) create mode 100644 config/samples/gatling-operator_v1alpha1_gatling03.yaml create mode 100644 gatling/sample/resources/goods_ids.csv create mode 100644 gatling/sample/resources/user_ids.csv create mode 100644 gatling/user-files/simulations/PersistentVolumeSampleSimulation.scala diff --git a/config/samples/gatling-operator_v1alpha1_gatling03.yaml b/config/samples/gatling-operator_v1alpha1_gatling03.yaml new file mode 100644 index 0000000..11c844f --- /dev/null +++ b/config/samples/gatling-operator_v1alpha1_gatling03.yaml @@ -0,0 +1,134 @@ +apiVersion: gatling-operator.tech.zozo.com/v1alpha1 +kind: Gatling +metadata: + name: gatling-sample03 +spec: + generateReport: false # The flag of generating gatling report + generateLocalReport: false # The flag of generating gatling report for each pod + notifyReport: false # The flag of notifying gatling report + cleanupAfterJobDone: true # The flag of cleaning up gatling jobs resources after the job done + podSpec: + serviceAccountName: "gatling-operator-worker" + gatlingImage: ghcr.io/st-tech/gatling:latest # Optional. Default: ghcr.io/st-tech/gatling:latest. The image that will be used for Gatling container. + rcloneImage: rclone/rclone # Optional. Default: rclone/rclone:latest. The image that will be used for rclone conatiner. + resources: # Optional. Resources specifies the resource limits of the container. + limits: + cpu: "500m" + memory: "500Mi" + volumes: + - name: resource-vol + persistentVolumeClaim: + claimName: resource-pvc + affinity: # Optional. Affinity specification + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/os + operator: In + values: + - linux + ######################################################################################### + # PersistentVolume + ######################################################################################### + # To try this scenario with kind, add the extraMounts setting to config/kind/cluster.yaml + # --------------------------------------------------------------------------------------- + # kind: Cluster + # apiVersion: kind.x-k8s.io/v1alpha4 + # nodes: + # - role: control-plane + # - role: worker + # + extraMounts: + # + - hostPath: /Users + # + containerPath: /Users + ######################################################################################### + persistentVolume: + name: resource-pv + spec: + volumeMode: Filesystem + accessModes: + - ReadWriteOnce + storageClassName: "" + capacity: + storage: 1Gi + local: + path: /Users/xxxxxx/github/st-tech/gatling-operator/gatling/sample/resources # path of the local environment you want to mount as a persistent volume + nodeAffinity: + required: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/os + operator: In + values: + - linux + persistentVolumeClaim: + name: resource-pvc + spec: + accessModes: + - ReadWriteOnce + storageClassName: "" + volumeName: resource-pv + resources: + requests: + storage: 1Gi + cloudStorageSpec: + ############################################################# + # Storage Provider - aws (AMAZON S3) + ############################################################# + provider: "aws" # Provider specifies the cloud provider that will be used. Supported providers: "aws", "gcp", "azure" + bucket: "gatling-operator-reports" # S3 Bucket name on which Gatlilng report files are stored + region: "ap-northeast-1" # Optional. Default: "ap-northeast-1" for aws provider. Region name + #env: # Optional. Environment variables to be used for connecting to the cloud providers + # # For S3 see also the env variables for auth: https://rclone.org/s3/#authentication + # - name: AWS_ACCESS_KEY_ID + # value: xxxxxxxxxxxxxxx + # - name: AWS_SECRET_ACCESS_KEY + # valueFrom: + # secretKeyRef: + # name: aws-credentail-secrets + # key: AWS_SECRET_ACCESS_KEY + # + ############################################################# + # Storage Provider - gcp (Google Cloud Storage) + ############################################################# + # provider: "gcp" + # bucket: "gatling-operator-reports" # GCS bucket name on which Gatlilng report files are stored + # + ############################################################# + # Storage Provider - azure (Azure Blob Storage) + ############################################################# + #provider: "azure" + #bucket: "gatling-operator-reports" # Azure Blob Storage container name on which Gatlilng report files are stored + #env: # Optional. Environment variables to be used for connecting to the cloud providers + # - name: AZUREBLOB_ACCOUNT # Azure Blob Storage Account Name + # value: xxxxxxxxxxxxxxx + # - name: AZUREBLOB_KEY # Azure Blob Access Key. Leave blank to use SAS URL + # valueFrom: + # secretKeyRef: + # name: azure-credentail-secrets + # key: AZUREBLOB_KEY + # - name: AZUREBLOB_SAS_URL # SAS URL. "Read, Write, List" permissions are required + # valueFrom: + # secretKeyRef: + # name: azure-credentail-secrets + # key: AZUREBLOB_SAS_URL + notificationServiceSpec: + provider: "slack" # Notification provider name. Supported provider: "slack" + secretName: "gatling-notification-slack-secrets" # The name of secret in which all key/value sets needed for the notification are stored + testScenarioSpec: + # startTime: 2021-09-10 08:45:31 # Optional. Start time of running test scenario in UTC. Format: %Y-%m-%d %H:%M:%S + parallelism: 3 # Optional. Default: 1. Number of pods running at any instant + # simulationsDirectoryPath: "/dir-path-to-simulation" # Optional. Default: /opt/gatling/user-files/simulations + # resourcesDirectoryPath: "dir-path-to-resources" # Optional. Default: /opt/gatling/user-files/resources + # resultsDirectoryPath: "dir-path-to-results" # Optional. Default: /opt/gatling/results. + simulationClass: "PersistentVolumeSampleSimulation" # Gatling simulation class name + env: + - name: ENV + value: "dev" + - name: CONCURRENCY + value: "2" + - name: DURATION + value: "10" + volumeMounts: + - name: resource-vol + mountPath: /opt/gatling/user-files/resources/pv diff --git a/gatling/sample/resources/goods_ids.csv b/gatling/sample/resources/goods_ids.csv new file mode 100644 index 0000000..0b1029c --- /dev/null +++ b/gatling/sample/resources/goods_ids.csv @@ -0,0 +1,6 @@ +goods_id,goods_name +1,hoge +2,fuga +3,sample +4,test +5,goods diff --git a/gatling/sample/resources/user_ids.csv b/gatling/sample/resources/user_ids.csv new file mode 100644 index 0000000..d05828d --- /dev/null +++ b/gatling/sample/resources/user_ids.csv @@ -0,0 +1,6 @@ +user_id +1 +2 +3 +4 +5 diff --git a/gatling/user-files/resources/myresources.csv b/gatling/user-files/resources/myresources.csv index 9df02fe..3b634ff 100644 --- a/gatling/user-files/resources/myresources.csv +++ b/gatling/user-files/resources/myresources.csv @@ -1,3 +1,4 @@ +alphabet "a" "b" "c" diff --git a/gatling/user-files/simulations/PersistentVolumeSampleSimulation.scala b/gatling/user-files/simulations/PersistentVolumeSampleSimulation.scala new file mode 100644 index 0000000..e1fa24a --- /dev/null +++ b/gatling/user-files/simulations/PersistentVolumeSampleSimulation.scala @@ -0,0 +1,49 @@ +// Sample originally from https://github.com/gatling/gatling/blob/400a125d7995d1b895c4cc4847ff15059d252948/gatling-bundle/src/main/scala/computerdatabase/BasicSimulation.scala +/* +* Copyright 2011-2021 GatlingCorp (https://gatling.io) +* +* 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. +*/ +import scala.concurrent.duration._ + +import io.gatling.core.Predef._ +import io.gatling.http.Predef._ + +class PersistentVolumeSampleSimulation extends Simulation { + + val usersPerSec = sys.env.getOrElse("CONCURRENCY", "1").toInt + val durationSec = sys.env.getOrElse("DURATION", "10").toInt + + val feeder_user_ids = csv("pv/user_ids.csv") + val feeder_goods_ids = csv("pv/goods_ids.csv") + val feeder_myresources = csv("myresources.csv") + + // A scenario is a chain of requests and pauses + val scn = scenario("Scenario Name") + .feed(feeder_user_ids.circular) + .feed(feeder_goods_ids.circular) + .feed(feeder_myresources.circular) + .exec { session => + println(s"User ${session("user_id").as[String]} is buying goods ${session("goods_id").as[String]} ${session("goods_name").as[String]}") + println(s"myresource: ${session("alphabet").as[String]}") + session + } + // Note that Gatling has recorded real time pauses + .pause(1) + + setUp( + scn.inject( + constantUsersPerSec(usersPerSec) during(durationSec seconds) + ) + ) +} From 5001aeab8f5b9e003eab1428c891eff1e7f5d7e4 Mon Sep 17 00:00:00 2001 From: kane8n Date: Fri, 1 Mar 2024 20:04:49 +0900 Subject: [PATCH 5/8] update user-guide --- .../gatling-operator_v1alpha1_gatling03.yaml | 2 +- docs/user-guide.md | 56 ++++++++++++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/config/samples/gatling-operator_v1alpha1_gatling03.yaml b/config/samples/gatling-operator_v1alpha1_gatling03.yaml index 11c844f..725f000 100644 --- a/config/samples/gatling-operator_v1alpha1_gatling03.yaml +++ b/config/samples/gatling-operator_v1alpha1_gatling03.yaml @@ -121,7 +121,7 @@ spec: # simulationsDirectoryPath: "/dir-path-to-simulation" # Optional. Default: /opt/gatling/user-files/simulations # resourcesDirectoryPath: "dir-path-to-resources" # Optional. Default: /opt/gatling/user-files/resources # resultsDirectoryPath: "dir-path-to-results" # Optional. Default: /opt/gatling/results. - simulationClass: "PersistentVolumeSampleSimulation" # Gatling simulation class name + simulationClass: "PersistentVolumeSampleSimulation" # Gatling simulation class name env: - name: ENV value: "dev" diff --git a/docs/user-guide.md b/docs/user-guide.md index 528e4a2..c2228c3 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -7,6 +7,7 @@ - [Gatling Load Testing Configuration and Deployment](#gatling-load-testing-configuration-and-deployment) - [Create Custom Gatling Image to bundle Gatling Load Testing Files](#create-custom-gatling-image-to-bundle-gatling-load-testing-files) - [Add Gatling Load Testing Files in Gatling CR](#add-gatling-load-testing-files-in-gatling-cr) + - [Read Gatling Load Testing Files from PersistentVolume](#read-gatling-load-testing-files-from-persistentVolume) - [Debug and Trace Gatling Load Testing](#debug-and-trace-gatling-load-testing) - [How to Pull Gatling Runtime Image from Private Registry](#how-to-pull-gatling-runtime-image-from-private-registry) - [Create an "image pull secret"](#create-an-image-pull-secret) @@ -50,10 +51,11 @@ As described in Configuration Overview, there are 2 things that you need to cons For `Gatling docker image`, you can use default image `ghcr.io/st-tech/gatling:latest`, or you can create custom image to use. -For `Gatling load testing related files`, you have 2 options: +For `Gatling load testing related files`, you have 3 options: - Create custom image to bundle Gatling load testing files with Java runtime and Gatling standalone bundle package - Add Gatling load testing files as multi-line definitions in `.spec.testScenatioSpec` part of `Gatling CR` +- Set up persistent volumes in `.persistentVolume` and `.persistentVolumeClaim` in `Gatling CR` and load test files from the persistent volumes in Gatling load test files. ### Create Custom Gatling Image to bundle Gatling Load Testing Files @@ -205,6 +207,58 @@ For a full sample manifest, please check [this](../config/samples/gatling-operat 📝 **Caution**: Please be noted that the data stored in a ConfigMap cannot exceed 1 MiB (ref [this](https://kubernetes.io/docs/concepts/configuration/configmap/)). If you need to store files that are larger than this limit, you may want to consider create Custom Gatling Image to bundle them in the container. +### Read Gatling Load Testing Files from PersistentVolume + +You can place Gatling load testing files in a [Kubernetes persistent volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) and read them from there. When you configure the required settings in `.spec.persistentVolume` and `.spec.persistentVolumeClaim`, Gatling Controller automatically creates the `PersistentVolume` and `PersistentVolumeClaim` resources in the cluster. + +If you're configuring Amazon EFS as a persistent volume, like this: +```yaml +kind: StorageClass +apiVersion: storage.k8s.io/v1 +metadata: + name: efs-sc +provisioner: efs.csi.aws.com + +--- + +apiVersion: gatling-operator.tech.zozo.com/v1alpha1 +kind: Gatling +metadata: + name: gatling-sample +spec: + podSpec: + volumes: + - name: efs-vol + persistentVolumeClaim: + claimName: efs-pvc + persistentVolume: + name: efs-pv + spec: + capacity: + storage: 1Gi + accessModes: + - ReadWriteMany + storageClassName: efs-sc + csi: + driver: efs.csi.aws.com + volumeHandle: fs-xxxxxx + persistentVolumeClaim: + name: efs-pvc + spec: + accessModes: + - ReadWriteMany + storageClassName: efs-sc + resources: + requests: + storage: 1Gi + testScenarioSpec: + volumeMounts: + - name: efs-vol + mountPath: /opt/gatling/user-files +``` + +Assuming that `StorageClass` resources is created in advance, and if `PersistentVolume` resources are also created in advance, configuring only `podSec.volumes` and `testScenarioSpec.volumeMounts` settings will be sufficient for operation. + ### Debug and Trace Gatling Load Testing As you can see in the section of [Create Custom Gatling Image to bundle Gatling Load Testing Files](#create-custom-gatling-image-to-bundle-gatling-load-testing-files), you can check the logging output of each Gatling load testing via container log. But if you want to know more details on what's going on in Gatling load testing, you can leverage `logback.xml`. From c46d429830408425a0e58f532cad9ba603be9acc Mon Sep 17 00:00:00 2001 From: kane8n Date: Wed, 6 Mar 2024 19:00:43 +0900 Subject: [PATCH 6/8] add test code --- controllers/gatling_controller_test.go | 96 ++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/controllers/gatling_controller_test.go b/controllers/gatling_controller_test.go index 86484d3..eac9f91 100644 --- a/controllers/gatling_controller_test.go +++ b/controllers/gatling_controller_test.go @@ -10,6 +10,8 @@ import ( gatlingv1alpha1 "github.com/st-tech/gatling-operator/api/v1alpha1" batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/pointer" //+kubebuilder:scaffold:imports @@ -84,5 +86,99 @@ var _ = Context("Inside of a new namespace", func() { Expect(job.Spec.Completions).Should(Equal(pointer.Int32Ptr(2))) }) + It("shoud create a New Gatling resource with PersistentVolume resources", func() { + pvFS := corev1.PersistentVolumeFilesystem + gatling := &gatlingv1alpha1.Gatling{ + ObjectMeta: metav1.ObjectMeta{ + Name: gatlingName, + Namespace: ns.Name, + }, + Spec: gatlingv1alpha1.GatlingSpec{ + GenerateReport: false, + NotifyReport: false, + CleanupAfterJobDone: false, + PodSpec: gatlingv1alpha1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: "resource-vol", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "resource-pvc", + }, + }, + }, + }, + }, + PersistentVolumeSpec: gatlingv1alpha1.PersistentVolumeSpec{ + Name: "resource-pv", + Spec: corev1.PersistentVolumeSpec{ + VolumeMode: &pvFS, + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + StorageClassName: "", + Capacity: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse("1Gi"), + }, + PersistentVolumeSource: corev1.PersistentVolumeSource{ + Local: &corev1.LocalVolumeSource{Path: "/tmp"}, + }, + NodeAffinity: &corev1.VolumeNodeAffinity{ + Required: &corev1.NodeSelector{ + NodeSelectorTerms: []corev1.NodeSelectorTerm{ + { + MatchExpressions: []corev1.NodeSelectorRequirement{{Key: "kubernetes.io/os", Operator: corev1.NodeSelectorOpIn, Values: []string{"linux"}}}, + }, + }, + }, + }, + }, + }, + PersistentVolumeClaimSpec: gatlingv1alpha1.PersistentVolumeClaimSpec{ + Name: "resource-pvc", + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + VolumeName: "resource-pv", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{corev1.ResourceStorage: resource.MustParse("1Gi")}, + }, + }, + }, + TestScenarioSpec: gatlingv1alpha1.TestScenarioSpec{ + SimulationClass: "PersistentVolumeSampleSimulation", + Parallelism: 1, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "resource-vol", + MountPath: "/opt/gatling/user-files/resources/pv", + }, + }, + }, + }, + } + err := k8sClient.Create(ctx, gatling) + Expect(err).NotTo(HaveOccurred(), "failed to create test Gatling resource") + + job := &batchv1.Job{} + Eventually(func() error { + return k8sClient.Get( + ctx, client.ObjectKey{Namespace: ns.Name, Name: gatlingName + "-runner"}, job) + }).Should(Succeed()) + + pv := &corev1.PersistentVolume{} + Eventually(func() error { + return k8sClient.Get( + ctx, client.ObjectKey{Namespace: ns.Name, Name: "resource-pv"}, pv) + }).Should(Succeed()) + + pvc := &corev1.PersistentVolumeClaim{} + Eventually(func() error { + return k8sClient.Get( + ctx, client.ObjectKey{Namespace: ns.Name, Name: "resource-pvc"}, pvc) + }).Should(Succeed()) + }) + }) }) From 2a9dc4e747268a2e12751fe2a56a853348982642 Mon Sep 17 00:00:00 2001 From: kane8n Date: Wed, 6 Mar 2024 19:49:34 +0900 Subject: [PATCH 7/8] fix based on review comments --- api/v1alpha1/groupversion_info.go | 4 ++-- controllers/gatling_controller.go | 12 ++++++------ docs/user-guide.md | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/v1alpha1/groupversion_info.go b/api/v1alpha1/groupversion_info.go index 1541035..2565676 100644 --- a/api/v1alpha1/groupversion_info.go +++ b/api/v1alpha1/groupversion_info.go @@ -15,8 +15,8 @@ limitations under the License. */ // Package v1alpha1 contains API Schema definitions for the gatling-operator v1alpha1 API group -//+kubebuilder:object:generate=true -//+groupName=gatling-operator.tech.zozo.com +// +kubebuilder:object:generate=true +// +groupName=gatling-operator.tech.zozo.com package v1alpha1 import ( diff --git a/controllers/gatling_controller.go b/controllers/gatling_controller.go index 35463ce..18b7b37 100644 --- a/controllers/gatling_controller.go +++ b/controllers/gatling_controller.go @@ -143,7 +143,7 @@ func (r *GatlingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct // Implementation of reconciler logic for the runner job func (r *GatlingReconciler) gatlingRunnerReconcile(ctx context.Context, req ctrl.Request, gatling *gatlingv1alpha1.Gatling, log logr.Logger) (bool, error) { - if err := r.createObjectsForCR(ctx, gatling, req.Namespace, log); err != nil { + if err := r.createVolumesForCR(ctx, gatling, req.Namespace, log); err != nil { return true, err } if gatling.Status.RunnerJobName == "" { @@ -370,7 +370,7 @@ func doNotRequeue(err error) (ctrl.Result, error) { return ctrl.Result{}, err } -func (r *GatlingReconciler) createObjectsForCR(ctx context.Context, gatling *gatlingv1alpha1.Gatling, namespace string, log logr.Logger) error { +func (r *GatlingReconciler) createVolumesForCR(ctx context.Context, gatling *gatlingv1alpha1.Gatling, namespace string, log logr.Logger) error { // Create Simulation Data ConfigMap if defined to create in CR if &gatling.Spec.TestScenarioSpec.SimulationData != nil && len(gatling.Spec.TestScenarioSpec.SimulationData) > 0 { configMapName := gatling.Name + "-simulations-data" @@ -419,14 +419,14 @@ func (r *GatlingReconciler) createObjectsForCR(ctx context.Context, gatling *gat // Create PersistentVolume if defined to create in CR if &gatling.Spec.PersistentVolumeSpec != nil && gatling.Spec.PersistentVolumeSpec.Name != "" { volumeName := gatling.Spec.PersistentVolumeSpec.Name - foundVolue := &corev1.PersistentVolume{} - if err := r.Get(ctx, client.ObjectKey{Name: volumeName, Namespace: namespace}, foundVolue); err != nil { + foundVolume := &corev1.PersistentVolume{} + if err := r.Get(ctx, client.ObjectKey{Name: volumeName, Namespace: namespace}, foundVolume); err != nil { if !apierr.IsNotFound(err) { return err } persistentVolume := r.newPersistentVolumeForCR(gatling, volumeName, &gatling.Spec.PersistentVolumeSpec.Spec) if err := r.createObject(ctx, gatling, persistentVolume); err != nil { - log.Error(err, fmt.Sprintf("Failed to creating new PersistentVolume: namespace %s name %s", gatling.GetNamespace(), volumeName)) + log.Error(err, fmt.Sprintf("Failed to creating new PersistentVolume: namespace %s name %s", persistentVolume.GetNamespace(), volumeName)) return err } } @@ -441,7 +441,7 @@ func (r *GatlingReconciler) createObjectsForCR(ctx context.Context, gatling *gat } persistentVolumeClaim := r.newPersistentVolumeClaimForCR(gatling, claimName, &gatling.Spec.PersistentVolumeClaimSpec.Spec) if err := r.createObject(ctx, gatling, persistentVolumeClaim); err != nil { - log.Error(err, fmt.Sprintf("Failed to creating new PersistentVolumeClaim: namespace %s name %s", gatling.GetNamespace(), claimName)) + log.Error(err, fmt.Sprintf("Failed to creating new PersistentVolumeClaim: namespace %s name %s", persistentVolumeClaim.GetNamespace(), claimName)) return err } } diff --git a/docs/user-guide.md b/docs/user-guide.md index c2228c3..dcf7c55 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -55,7 +55,7 @@ For `Gatling load testing related files`, you have 3 options: - Create custom image to bundle Gatling load testing files with Java runtime and Gatling standalone bundle package - Add Gatling load testing files as multi-line definitions in `.spec.testScenatioSpec` part of `Gatling CR` -- Set up persistent volumes in `.persistentVolume` and `.persistentVolumeClaim` in `Gatling CR` and load test files from the persistent volumes in Gatling load test files. +- Set up persistent volume in `.persistentVolume` and `.persistentVolumeClaim` in `Gatling CR` and load test files from the persistent volume in Gatling load test files. ### Create Custom Gatling Image to bundle Gatling Load Testing Files From dae80d0b4f830defd4979c5a6047866a2515760a Mon Sep 17 00:00:00 2001 From: kane8n Date: Thu, 21 Mar 2024 20:31:02 +0900 Subject: [PATCH 8/8] fix typo --- controllers/gatling_controller_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/gatling_controller_test.go b/controllers/gatling_controller_test.go index eac9f91..6515d09 100644 --- a/controllers/gatling_controller_test.go +++ b/controllers/gatling_controller_test.go @@ -86,7 +86,7 @@ var _ = Context("Inside of a new namespace", func() { Expect(job.Spec.Completions).Should(Equal(pointer.Int32Ptr(2))) }) - It("shoud create a New Gatling resource with PersistentVolume resources", func() { + It("should create a New Gatling resource with PersistentVolume resources", func() { pvFS := corev1.PersistentVolumeFilesystem gatling := &gatlingv1alpha1.Gatling{ ObjectMeta: metav1.ObjectMeta{