improve logical backup comparison unit test and improve container sync (#2686)
* improve logical backup comparison unit test and improve container sync * add new comparison function for volume mounts + unit test
This commit is contained in:
parent
37d6993439
commit
e71891e2bd
|
|
@ -597,7 +597,7 @@ func (c *Cluster) compareContainers(description string, setA, setB []v1.Containe
|
||||||
newCheck("new %s's %s (index %d) security context does not match the current one",
|
newCheck("new %s's %s (index %d) security context does not match the current one",
|
||||||
func(a, b v1.Container) bool { return !reflect.DeepEqual(a.SecurityContext, b.SecurityContext) }),
|
func(a, b v1.Container) bool { return !reflect.DeepEqual(a.SecurityContext, b.SecurityContext) }),
|
||||||
newCheck("new %s's %s (index %d) volume mounts do not match the current one",
|
newCheck("new %s's %s (index %d) volume mounts do not match the current one",
|
||||||
func(a, b v1.Container) bool { return !reflect.DeepEqual(a.VolumeMounts, b.VolumeMounts) }),
|
func(a, b v1.Container) bool { return !compareVolumeMounts(a.VolumeMounts, b.VolumeMounts) }),
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.OpConfig.EnableLazySpiloUpgrade {
|
if !c.OpConfig.EnableLazySpiloUpgrade {
|
||||||
|
|
@ -738,6 +738,27 @@ func comparePorts(a, b []v1.ContainerPort) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func compareVolumeMounts(old, new []v1.VolumeMount) bool {
|
||||||
|
if len(old) != len(new) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, mount := range old {
|
||||||
|
if !volumeMountExists(mount, new) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func volumeMountExists(mount v1.VolumeMount, mounts []v1.VolumeMount) bool {
|
||||||
|
for _, m := range mounts {
|
||||||
|
if reflect.DeepEqual(mount, m) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Cluster) compareAnnotations(old, new map[string]string) (bool, string) {
|
func (c *Cluster) compareAnnotations(old, new map[string]string) (bool, string) {
|
||||||
reason := ""
|
reason := ""
|
||||||
ignoredAnnotations := make(map[string]bool)
|
ignoredAnnotations := make(map[string]bool)
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,11 @@ import (
|
||||||
"github.com/zalando/postgres-operator/pkg/util/config"
|
"github.com/zalando/postgres-operator/pkg/util/config"
|
||||||
"github.com/zalando/postgres-operator/pkg/util/constants"
|
"github.com/zalando/postgres-operator/pkg/util/constants"
|
||||||
"github.com/zalando/postgres-operator/pkg/util/k8sutil"
|
"github.com/zalando/postgres-operator/pkg/util/k8sutil"
|
||||||
|
"github.com/zalando/postgres-operator/pkg/util/patroni"
|
||||||
"github.com/zalando/postgres-operator/pkg/util/teams"
|
"github.com/zalando/postgres-operator/pkg/util/teams"
|
||||||
batchv1 "k8s.io/api/batch/v1"
|
batchv1 "k8s.io/api/batch/v1"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
|
|
@ -1464,7 +1466,7 @@ func TestCompareServices(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCronJob(image, schedule string, vars []v1.EnvVar) *batchv1.CronJob {
|
func newCronJob(image, schedule string, vars []v1.EnvVar, mounts []v1.VolumeMount) *batchv1.CronJob {
|
||||||
cron := &batchv1.CronJob{
|
cron := &batchv1.CronJob{
|
||||||
Spec: batchv1.CronJobSpec{
|
Spec: batchv1.CronJobSpec{
|
||||||
Schedule: schedule,
|
Schedule: schedule,
|
||||||
|
|
@ -1477,6 +1479,37 @@ func newCronJob(image, schedule string, vars []v1.EnvVar) *batchv1.CronJob {
|
||||||
Name: "logical-backup",
|
Name: "logical-backup",
|
||||||
Image: image,
|
Image: image,
|
||||||
Env: vars,
|
Env: vars,
|
||||||
|
Ports: []v1.ContainerPort{
|
||||||
|
{
|
||||||
|
ContainerPort: patroni.ApiPort,
|
||||||
|
Protocol: v1.ProtocolTCP,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ContainerPort: pgPort,
|
||||||
|
Protocol: v1.ProtocolTCP,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ContainerPort: operatorPort,
|
||||||
|
Protocol: v1.ProtocolTCP,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Resources: v1.ResourceRequirements{
|
||||||
|
Requests: v1.ResourceList{
|
||||||
|
v1.ResourceCPU: resource.MustParse("100m"),
|
||||||
|
v1.ResourceMemory: resource.MustParse("100Mi"),
|
||||||
|
},
|
||||||
|
Limits: v1.ResourceList{
|
||||||
|
v1.ResourceCPU: resource.MustParse("100m"),
|
||||||
|
v1.ResourceMemory: resource.MustParse("100Mi"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SecurityContext: &v1.SecurityContext{
|
||||||
|
AllowPrivilegeEscalation: nil,
|
||||||
|
Privileged: util.False(),
|
||||||
|
ReadOnlyRootFilesystem: util.False(),
|
||||||
|
Capabilities: nil,
|
||||||
|
},
|
||||||
|
VolumeMounts: mounts,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -1493,37 +1526,110 @@ func TestCompareLogicalBackupJob(t *testing.T) {
|
||||||
img1 := "registry.opensource.zalan.do/acid/logical-backup:v1.0"
|
img1 := "registry.opensource.zalan.do/acid/logical-backup:v1.0"
|
||||||
img2 := "registry.opensource.zalan.do/acid/logical-backup:v2.0"
|
img2 := "registry.opensource.zalan.do/acid/logical-backup:v2.0"
|
||||||
|
|
||||||
|
clientSet := fake.NewSimpleClientset()
|
||||||
|
acidClientSet := fakeacidv1.NewSimpleClientset()
|
||||||
|
namespace := "default"
|
||||||
|
|
||||||
|
client := k8sutil.KubernetesClient{
|
||||||
|
CronJobsGetter: clientSet.BatchV1(),
|
||||||
|
PostgresqlsGetter: acidClientSet.AcidV1(),
|
||||||
|
}
|
||||||
|
pg := acidv1.Postgresql{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "acid-cron-cluster",
|
||||||
|
Namespace: namespace,
|
||||||
|
},
|
||||||
|
Spec: acidv1.PostgresSpec{
|
||||||
|
Volume: acidv1.Volume{
|
||||||
|
Size: "1Gi",
|
||||||
|
},
|
||||||
|
EnableLogicalBackup: true,
|
||||||
|
LogicalBackupSchedule: "0 0 * * *",
|
||||||
|
LogicalBackupRetention: "3 months",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var cluster = New(
|
||||||
|
Config{
|
||||||
|
OpConfig: config.Config{
|
||||||
|
PodManagementPolicy: "ordered_ready",
|
||||||
|
Resources: config.Resources{
|
||||||
|
ClusterLabels: map[string]string{"application": "spilo"},
|
||||||
|
ClusterNameLabel: "cluster-name",
|
||||||
|
DefaultCPURequest: "300m",
|
||||||
|
DefaultCPULimit: "300m",
|
||||||
|
DefaultMemoryRequest: "300Mi",
|
||||||
|
DefaultMemoryLimit: "300Mi",
|
||||||
|
PodRoleLabel: "spilo-role",
|
||||||
|
},
|
||||||
|
LogicalBackup: config.LogicalBackup{
|
||||||
|
LogicalBackupSchedule: "30 00 * * *",
|
||||||
|
LogicalBackupDockerImage: img1,
|
||||||
|
LogicalBackupJobPrefix: "logical-backup-",
|
||||||
|
LogicalBackupCPURequest: "100m",
|
||||||
|
LogicalBackupCPULimit: "100m",
|
||||||
|
LogicalBackupMemoryRequest: "100Mi",
|
||||||
|
LogicalBackupMemoryLimit: "100Mi",
|
||||||
|
LogicalBackupProvider: "s3",
|
||||||
|
LogicalBackupS3Bucket: "testBucket",
|
||||||
|
LogicalBackupS3BucketPrefix: "spilo",
|
||||||
|
LogicalBackupS3Region: "eu-central-1",
|
||||||
|
LogicalBackupS3Endpoint: "https://s3.amazonaws.com",
|
||||||
|
LogicalBackupS3AccessKeyID: "access",
|
||||||
|
LogicalBackupS3SecretAccessKey: "secret",
|
||||||
|
LogicalBackupS3SSE: "aws:kms",
|
||||||
|
LogicalBackupS3RetentionTime: "3 months",
|
||||||
|
LogicalBackupCronjobEnvironmentSecret: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, client, pg, logger, eventRecorder)
|
||||||
|
|
||||||
|
desiredCronJob, err := cluster.generateLogicalBackupJob()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Could not generate logical backup job with error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cluster.createLogicalBackupJob()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Could not create logical backup job with error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentCronJob, err := cluster.KubeClient.CronJobs(namespace).Get(context.TODO(), cluster.getLogicalBackupJobName(), metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Could not create logical backup job with error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
about string
|
about string
|
||||||
current *batchv1.CronJob
|
cronjob *batchv1.CronJob
|
||||||
new *batchv1.CronJob
|
|
||||||
match bool
|
match bool
|
||||||
reason string
|
reason string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
about: "two equal cronjobs",
|
about: "two equal cronjobs",
|
||||||
current: newCronJob(img1, "0 0 * * *", []v1.EnvVar{}),
|
cronjob: newCronJob(img1, "0 0 * * *", []v1.EnvVar{}, []v1.VolumeMount{}),
|
||||||
new: newCronJob(img1, "0 0 * * *", []v1.EnvVar{}),
|
|
||||||
match: true,
|
match: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
about: "two cronjobs with different image",
|
about: "two cronjobs with different image",
|
||||||
current: newCronJob(img1, "0 0 * * *", []v1.EnvVar{}),
|
cronjob: newCronJob(img2, "0 0 * * *", []v1.EnvVar{}, []v1.VolumeMount{}),
|
||||||
new: newCronJob(img2, "0 0 * * *", []v1.EnvVar{}),
|
|
||||||
match: false,
|
match: false,
|
||||||
reason: fmt.Sprintf("new job's image %q does not match the current one %q", img2, img1),
|
reason: fmt.Sprintf("new job's image %q does not match the current one %q", img2, img1),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
about: "two cronjobs with different schedule",
|
about: "two cronjobs with different schedule",
|
||||||
current: newCronJob(img1, "0 0 * * *", []v1.EnvVar{}),
|
cronjob: newCronJob(img1, "0 * * * *", []v1.EnvVar{}, []v1.VolumeMount{}),
|
||||||
new: newCronJob(img1, "0 * * * *", []v1.EnvVar{}),
|
|
||||||
match: false,
|
match: false,
|
||||||
reason: fmt.Sprintf("new job's schedule %q does not match the current one %q", "0 * * * *", "0 0 * * *"),
|
reason: fmt.Sprintf("new job's schedule %q does not match the current one %q", "0 * * * *", "0 0 * * *"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
about: "two cronjobs with empty and nil volume mounts",
|
||||||
|
cronjob: newCronJob(img1, "0 0 * * *", []v1.EnvVar{}, nil),
|
||||||
|
match: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
about: "two cronjobs with different environment variables",
|
about: "two cronjobs with different environment variables",
|
||||||
current: newCronJob(img1, "0 0 * * *", []v1.EnvVar{{Name: "LOGICAL_BACKUP_S3_BUCKET_PREFIX", Value: "spilo"}}),
|
cronjob: newCronJob(img1, "0 0 * * *", []v1.EnvVar{{Name: "LOGICAL_BACKUP_S3_BUCKET_PREFIX", Value: "logical-backup"}}, []v1.VolumeMount{}),
|
||||||
new: newCronJob(img1, "0 0 * * *", []v1.EnvVar{{Name: "LOGICAL_BACKUP_S3_BUCKET_PREFIX", Value: "logical-backup"}}),
|
|
||||||
match: false,
|
match: false,
|
||||||
reason: "logical backup container specs do not match: new cronjob container's logical-backup (index 0) environment does not match the current one",
|
reason: "logical backup container specs do not match: new cronjob container's logical-backup (index 0) environment does not match the current one",
|
||||||
},
|
},
|
||||||
|
|
@ -1531,9 +1637,21 @@ func TestCompareLogicalBackupJob(t *testing.T) {
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.about, func(t *testing.T) {
|
t.Run(tt.about, func(t *testing.T) {
|
||||||
match, reason := cl.compareLogicalBackupJob(tt.current, tt.new)
|
desiredCronJob.Spec.Schedule = tt.cronjob.Spec.Schedule
|
||||||
|
desiredCronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Image = tt.cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Image
|
||||||
|
desiredCronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].VolumeMounts = tt.cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].VolumeMounts
|
||||||
|
|
||||||
|
for _, testEnv := range tt.cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Env {
|
||||||
|
for i, env := range desiredCronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Env {
|
||||||
|
if env.Name == testEnv.Name {
|
||||||
|
desiredCronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Env[i] = testEnv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match, reason := cluster.compareLogicalBackupJob(currentCronJob, desiredCronJob)
|
||||||
if match != tt.match {
|
if match != tt.match {
|
||||||
t.Errorf("%s - unexpected match result %t when comparing cronjobs %q and %q", t.Name(), match, tt.current, tt.new)
|
t.Errorf("%s - unexpected match result %t when comparing cronjobs %#v and %#v", t.Name(), match, currentCronJob, desiredCronJob)
|
||||||
} else {
|
} else {
|
||||||
if !strings.HasPrefix(reason, tt.reason) {
|
if !strings.HasPrefix(reason, tt.reason) {
|
||||||
t.Errorf("%s - expected reason prefix %s, found %s", t.Name(), tt.reason, reason)
|
t.Errorf("%s - expected reason prefix %s, found %s", t.Name(), tt.reason, reason)
|
||||||
|
|
@ -1728,3 +1846,183 @@ func TestComparePorts(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCompareVolumeMounts(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
mountsA []v1.VolumeMount
|
||||||
|
mountsB []v1.VolumeMount
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty vs nil",
|
||||||
|
mountsA: []v1.VolumeMount{},
|
||||||
|
mountsB: nil,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "both empty",
|
||||||
|
mountsA: []v1.VolumeMount{},
|
||||||
|
mountsB: []v1.VolumeMount{},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "same mounts",
|
||||||
|
mountsA: []v1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "data",
|
||||||
|
ReadOnly: false,
|
||||||
|
MountPath: "/data",
|
||||||
|
SubPath: "subdir",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mountsB: []v1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "data",
|
||||||
|
ReadOnly: false,
|
||||||
|
MountPath: "/data",
|
||||||
|
SubPath: "subdir",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "different mounts",
|
||||||
|
mountsA: []v1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "data",
|
||||||
|
ReadOnly: false,
|
||||||
|
MountPath: "/data",
|
||||||
|
SubPathExpr: "$(POD_NAME)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mountsB: []v1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "data",
|
||||||
|
ReadOnly: false,
|
||||||
|
MountPath: "/data",
|
||||||
|
SubPath: "subdir",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "one equal mount one different",
|
||||||
|
mountsA: []v1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "data",
|
||||||
|
ReadOnly: false,
|
||||||
|
MountPath: "/data",
|
||||||
|
SubPath: "subdir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "poddata",
|
||||||
|
ReadOnly: false,
|
||||||
|
MountPath: "/poddata",
|
||||||
|
SubPathExpr: "$(POD_NAME)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mountsB: []v1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "data",
|
||||||
|
ReadOnly: false,
|
||||||
|
MountPath: "/data",
|
||||||
|
SubPath: "subdir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "etc",
|
||||||
|
ReadOnly: true,
|
||||||
|
MountPath: "/etc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "same mounts, different order",
|
||||||
|
mountsA: []v1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "data",
|
||||||
|
ReadOnly: false,
|
||||||
|
MountPath: "/data",
|
||||||
|
SubPath: "subdir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "etc",
|
||||||
|
ReadOnly: true,
|
||||||
|
MountPath: "/etc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mountsB: []v1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "etc",
|
||||||
|
ReadOnly: true,
|
||||||
|
MountPath: "/etc",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "data",
|
||||||
|
ReadOnly: false,
|
||||||
|
MountPath: "/data",
|
||||||
|
SubPath: "subdir",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "new mounts added",
|
||||||
|
mountsA: []v1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "data",
|
||||||
|
ReadOnly: false,
|
||||||
|
MountPath: "/data",
|
||||||
|
SubPath: "subdir",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mountsB: []v1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "etc",
|
||||||
|
ReadOnly: true,
|
||||||
|
MountPath: "/etc",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "data",
|
||||||
|
ReadOnly: false,
|
||||||
|
MountPath: "/data",
|
||||||
|
SubPath: "subdir",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "one mount removed",
|
||||||
|
mountsA: []v1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "data",
|
||||||
|
ReadOnly: false,
|
||||||
|
MountPath: "/data",
|
||||||
|
SubPath: "subdir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "etc",
|
||||||
|
ReadOnly: true,
|
||||||
|
MountPath: "/etc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mountsB: []v1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "data",
|
||||||
|
ReadOnly: false,
|
||||||
|
MountPath: "/data",
|
||||||
|
SubPath: "subdir",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range testCases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := compareVolumeMounts(tt.mountsA, tt.mountsB)
|
||||||
|
assert.Equal(t, tt.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -892,7 +892,7 @@ func (c *Cluster) generatePodTemplate(
|
||||||
addSecretVolume(&podSpec, additionalSecretMount, additionalSecretMountPath)
|
addSecretVolume(&podSpec, additionalSecretMount, additionalSecretMountPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if additionalVolumes != nil {
|
if len(additionalVolumes) > 0 {
|
||||||
c.addAdditionalVolumes(&podSpec, additionalVolumes)
|
c.addAdditionalVolumes(&podSpec, additionalVolumes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue