Skip to content

Commit

Permalink
Adding Openshift Service Account if shared config enabled
Browse files Browse the repository at this point in the history
  • Loading branch information
Shawn Hurley committed Nov 24, 2021
1 parent 15a0cb2 commit e3e7f5b
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 5 deletions.
3 changes: 3 additions & 0 deletions controllers/bsl.go
Expand Up @@ -99,6 +99,9 @@ func (r *DPAReconciler) ReconcileBackupStorageLocations(log logr.Logger) (bool,
}
bsl.Spec.BackupSyncPeriod = bslSpec.Bucket.BackupSyncPeriod
bsl.Spec.Config = bslSpec.Bucket.Config
if bucket.Spec.EnableSharedConfig {
bsl.Spec.Config["enableSharedConfig"] = "true"
}
bsl.Spec.Credential = bslSpec.Bucket.Credential
bsl.Spec.Default = bslSpec.Bucket.Default
bsl.Spec.Provider = AWSProvider
Expand Down
4 changes: 2 additions & 2 deletions controllers/dpa_controller.go
Expand Up @@ -85,8 +85,8 @@ func (r *DPAReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R
r.ValidateDataProtectionCR,
r.ReconcileVeleroSecurityContextConstraint,
r.ReconcileResticRestoreHelperConfig,
r.ValidateBackupStorage,
r.ReconcileBackupStorage,
r.ValidateBackupStorageLocations,
r.ReconcileBackupStorageLocations,
r.ReconcileRegistries,
r.ReconcileRegistrySVCs,
r.ReconcileRegistryRoutes,
Expand Down
63 changes: 61 additions & 2 deletions controllers/velero.go
Expand Up @@ -27,6 +27,7 @@ import (
"k8s.io/utils/pointer"

//"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

Expand Down Expand Up @@ -404,6 +405,8 @@ func (r *DPAReconciler) customizeVeleroDeployment(dpa *oadpv1alpha1.DataProtecti
veleroDeployment.Labels = r.getAppLabels(dpa)
veleroDeployment.Spec.Selector = veleroLabelSelector

isSTSNeeded := r.isSTSTokenNeeded(dpa.Spec.BackupLocations, dpa.Namespace)

//TODO: add velero nodeselector, needs to be added to the VELERO CR first
// Selector: veleroDeployment.Spec.Selector,
veleroDeployment.Spec.Replicas = pointer.Int32(1)
Expand All @@ -417,6 +420,30 @@ func (r *DPAReconciler) customizeVeleroDeployment(dpa *oadpv1alpha1.DataProtecti
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
})

if isSTSNeeded {
defaultMode := int32(420)
expirationSeconds := int64(3600)
veleroDeployment.Spec.Template.Spec.Volumes = append(veleroDeployment.Spec.Template.Spec.Volumes,
corev1.Volume{
Name: "bound-sa-token",
VolumeSource: corev1.VolumeSource{
Projected: &corev1.ProjectedVolumeSource{
DefaultMode: &defaultMode,
Sources: []corev1.VolumeProjection{
{
ServiceAccountToken: &corev1.ServiceAccountTokenProjection{
Audience: "openshift",
ExpirationSeconds: &expirationSeconds,
Path: "token",
},
},
},
},
},
},
)
}
//add any default init containers here if needed eg: setup-certificate-secret
// When you do this
// - please set the ImagePullPolicy to Always, and
Expand All @@ -438,13 +465,13 @@ func (r *DPAReconciler) customizeVeleroDeployment(dpa *oadpv1alpha1.DataProtecti
break
}
}
if err := r.customizeVeleroContainer(dpa, veleroDeployment, veleroContainer); err != nil {
if err := r.customizeVeleroContainer(dpa, veleroDeployment, veleroContainer, isSTSNeeded); err != nil {
return err
}
return credentials.AppendPluginSpecificSpecs(dpa, veleroDeployment, veleroContainer)
}

func (r *DPAReconciler) customizeVeleroContainer(dpa *oadpv1alpha1.DataProtectionApplication, veleroDeployment *appsv1.Deployment, veleroContainer *corev1.Container) error {
func (r *DPAReconciler) customizeVeleroContainer(dpa *oadpv1alpha1.DataProtectionApplication, veleroDeployment *appsv1.Deployment, veleroContainer *corev1.Container, isSTSNeeded bool) error {
if veleroContainer == nil {
return fmt.Errorf("could not find velero container in Deployment")
}
Expand All @@ -455,8 +482,18 @@ func (r *DPAReconciler) customizeVeleroContainer(dpa *oadpv1alpha1.DataProtectio
MountPath: "/etc/ssl/certs",
},
)

if isSTSNeeded {
veleroContainer.VolumeMounts = append(veleroContainer.VolumeMounts,
corev1.VolumeMount{
Name: "bound-sa-token",
MountPath: "/var/run/secrets/openshift/serviceaccount",
ReadOnly: true,
})
}
// Append proxy settings to the container from environment variables
veleroContainer.Env = append(veleroContainer.Env, proxy.ReadProxyVarsFromEnv()...)

// Enable user to specify --restic-timeout (defaults to 1h)
resticTimeout := "1h"
if dpa.Spec.Configuration.Restic != nil && len(dpa.Spec.Configuration.Restic.Timeout) > 0 {
Expand All @@ -468,6 +505,28 @@ func (r *DPAReconciler) customizeVeleroContainer(dpa *oadpv1alpha1.DataProtectio
return nil
}

func (r *DPAReconciler) isSTSTokenNeeded(bsls []oadpv1alpha1.BackupLocation, ns string) bool {

for _, bsl := range bsls {
if bsl.Bucket != nil {
bucket := &oadpv1alpha1.Bucket{}
err := r.Get(r.Context, client.ObjectKey{
Name: bsl.Bucket.BucketRef.Name,
Namespace: ns,
}, bucket)
if err != nil {
//log
return false
}
if bucket.Spec.EnableSharedConfig {
return true
}
}
}

return false
}

func getVeleroImage(dpa *oadpv1alpha1.DataProtectionApplication) string {
if dpa.Spec.UnsupportedOverrides[oadpv1alpha1.VeleroImageKey] != "" {
return dpa.Spec.UnsupportedOverrides[oadpv1alpha1.VeleroImageKey]
Expand Down
247 changes: 246 additions & 1 deletion controllers/velero_test.go
Expand Up @@ -36,6 +36,7 @@ func TestDPAReconciler_buildVeleroDeployment(t *testing.T) {
dpa *oadpv1alpha1.DataProtectionApplication
wantErr bool
wantVeleroDeployment *appsv1.Deployment
clientObjects []client.Object
}{
{
name: "DPA CR is nil",
Expand Down Expand Up @@ -930,10 +931,254 @@ func TestDPAReconciler_buildVeleroDeployment(t *testing.T) {
},
},
},
{
name: "given valid Velero CR with with aws plugin from bucket",
veleroDeployment: &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "test-velero-deployment",
Namespace: "test-ns",
},
Spec: appsv1.DeploymentSpec{
Selector: veleroLabelSelector,
},
},
dpa: &oadpv1alpha1.DataProtectionApplication{
ObjectMeta: metav1.ObjectMeta{
Name: "test-Velero-CR",
Namespace: "test-ns",
},
Spec: oadpv1alpha1.DataProtectionApplicationSpec{
Configuration: &oadpv1alpha1.ApplicationConfig{
Velero: &oadpv1alpha1.VeleroConfig{
DefaultPlugins: []oadpv1alpha1.DefaultPlugin{
oadpv1alpha1.DefaultPluginAWS,
},
},
},
BackupLocations: []oadpv1alpha1.BackupLocation{
{
Bucket: &oadpv1alpha1.BucketBackupLocation{
BucketRef: corev1.LocalObjectReference{
Name: "bucket-123",
},
Config: map[string]string{},
Credential: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "cloud-credentials",
},
Key: "creds",
},
Default: false,
BackupSyncPeriod: &metav1.Duration{},
},
},
},
},
},
wantErr: false,
wantVeleroDeployment: &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "test-velero-deployment",
Namespace: "test-ns",
Labels: map[string]string{
"app.kubernetes.io/name": common.Velero,
"app.kubernetes.io/instance": "test-Velero-CR",
"app.kubernetes.io/managed-by": common.OADPOperator,
"app.kubernetes.io/component": Server,
oadpv1alpha1.OadpOperatorLabel: "True",
},
},
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: appsv1.SchemeGroupVersion.String(),
},
Spec: appsv1.DeploymentSpec{
Selector: veleroLabelSelector,
Replicas: pointer.Int32(1),
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: veleroLabelSelector.MatchLabels,
Annotations: map[string]string{
"prometheus.io/scrape": "true",
"prometheus.io/port": "8085",
"prometheus.io/path": "/metrics",
},
},
Spec: corev1.PodSpec{
RestartPolicy: corev1.RestartPolicyAlways,
ServiceAccountName: common.Velero,
Containers: []corev1.Container{
{
Name: common.Velero,
Image: common.VeleroImage,
ImagePullPolicy: corev1.PullAlways,
Ports: []corev1.ContainerPort{
{
Name: "metrics",
ContainerPort: 8085,
},
},
Resources: corev1.ResourceRequirements{
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("1"),
corev1.ResourceMemory: resource.MustParse("512Mi"),
},
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("500m"),
corev1.ResourceMemory: resource.MustParse("128Mi"),
},
},
Command: []string{"/velero"},
Args: []string{
"server",
"--restic-timeout=1h",
},
VolumeMounts: []corev1.VolumeMount{
{
Name: "plugins",
MountPath: "/plugins",
},
{
Name: "scratch",
MountPath: "/scratch",
},
{
Name: "certs",
MountPath: "/etc/ssl/certs",
},
{
Name: "bound-sa-token",
MountPath: "/var/run/secrets/openshift/serviceaccount",
ReadOnly: true,
},
{
Name: "cloud-credentials",
MountPath: "/credentials",
},
},
Env: []corev1.EnvVar{
{
Name: common.VeleroScratchDirEnvKey,
Value: "/scratch",
},
{
Name: common.VeleroNamespaceEnvKey,
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{
FieldPath: "metadata.namespace",
},
},
},
{
Name: common.LDLibraryPathEnvKey,
Value: "/plugins",
},
{
Name: common.HTTPProxyEnvVar,
Value: os.Getenv("HTTP_PROXY"),
},
{
Name: common.HTTPSProxyEnvVar,
Value: os.Getenv("HTTPS_PROXY"),
},
{
Name: common.NoProxyEnvVar,
Value: os.Getenv("NO_PROXY"),
},
{
Name: common.AWSSharedCredentialsFileEnvKey,
Value: "/credentials/cloud",
},
},
},
},
Volumes: []corev1.Volume{
{
Name: "plugins",
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
},
{
Name: "scratch",
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
},
{
Name: "certs",
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
},
{
Name: "bound-sa-token",
VolumeSource: corev1.VolumeSource{
Projected: &corev1.ProjectedVolumeSource{
DefaultMode: pointer.Int32(420),
Sources: []corev1.VolumeProjection{
{
ServiceAccountToken: &corev1.ServiceAccountTokenProjection{
Audience: "openshift",
ExpirationSeconds: pointer.Int64(3600),
Path: "token",
},
},
},
},
},
},
{
Name: "cloud-credentials",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: "cloud-credentials",
},
},
},
},
InitContainers: []corev1.Container{
{
Image: common.AWSPluginImage,
Name: common.VeleroPluginForAWS,
ImagePullPolicy: corev1.PullAlways,
Resources: corev1.ResourceRequirements{},
TerminationMessagePath: "/dev/termination-log",
TerminationMessagePolicy: "File",
VolumeMounts: []corev1.VolumeMount{
{
MountPath: "/target",
Name: "plugins",
},
},
},
},
},
},
},
},
clientObjects: []client.Object{
&oadpv1alpha1.Bucket{
ObjectMeta: metav1.ObjectMeta{
Name: "bucket-123",
Namespace: "test-ns",
},
Spec: oadpv1alpha1.BucketSpec{
EnableSharedConfig: true,
},
},
},
},
}
r := &DPAReconciler{}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fakeClient, err := getFakeClientFromObjects(tt.clientObjects...)
if err != nil {
t.Errorf("error in creating fake client, likely programmer error")
}
r := DPAReconciler{
Client: fakeClient,
}
if err := r.buildVeleroDeployment(tt.veleroDeployment, tt.dpa); (err != nil) != tt.wantErr {
t.Errorf("buildVeleroDeployment() error = %v, wantErr %v", err, tt.wantErr)
}
Expand Down

0 comments on commit e3e7f5b

Please sign in to comment.