Skip to content

Commit

Permalink
update the operator master deployment to support workload identity
Browse files Browse the repository at this point in the history
This causes the spec for the operator master deployment to mount the
service account token as a volume, and maps the path to the environment
variable expected by Azure to support workload identities
  • Loading branch information
yithian committed Aug 27, 2024
1 parent fec517c commit 1c88479
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 3 deletions.
13 changes: 11 additions & 2 deletions pkg/operator/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ type deploymentData struct {
Version string
IsLocalDevelopment bool
SupportsPodSecurityAdmission bool
UsesWorkloadIdentity bool
TokenVolumeMountPath string
}

func templateManifests(data deploymentData) ([][]byte, error) {
Expand Down Expand Up @@ -160,12 +162,19 @@ func (o *operator) createDeploymentData(ctx context.Context) (deploymentData, er
return deploymentData{}, err
}

return deploymentData{
data := deploymentData{
IsLocalDevelopment: o.env.IsLocalDevelopmentMode(),
Image: image,
SupportsPodSecurityAdmission: usePodSecurityAdmission,
Version: version,
}, nil
}

if o.oc.UsesWorkloadIdentity() {
data.UsesWorkloadIdentity = o.oc.UsesWorkloadIdentity()
data.TokenVolumeMountPath = pkgoperator.OperatorTokenFile
}

return data, nil
}

func (o *operator) createObjects(ctx context.Context) ([]kruntime.Object, error) {
Expand Down
82 changes: 82 additions & 0 deletions pkg/operator/deploy/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
ctrlfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/yaml"

"github.com/Azure/ARO-RP/pkg/api"
pkgoperator "github.com/Azure/ARO-RP/pkg/operator"
Expand Down Expand Up @@ -203,6 +205,39 @@ func TestCreateDeploymentData(t *testing.T) {
SupportsPodSecurityAdmission: true,
},
},
{
name: "workload identity detected",
mock: func(env *mock_env.MockInterface, oc *api.OpenShiftCluster) {
env.EXPECT().
AROOperatorImage().
Return(operatorImageWithTag)
// set so that UsesWorkloadIdentity() returns true
oc.Properties.PlatformWorkloadIdentityProfile = &api.PlatformWorkloadIdentityProfile{}
},
clusterVersion: "4.10.0",
expected: deploymentData{
Image: operatorImageWithTag,
Version: operatorImageTag,
UsesWorkloadIdentity: true,
TokenVolumeMountPath: pkgoperator.OperatorTokenFile,
},
},
{
name: "service principal detected",
mock: func(env *mock_env.MockInterface, oc *api.OpenShiftCluster) {
env.EXPECT().
AROOperatorImage().
Return(operatorImageWithTag)
// set so that UsesWorkloadIdentity() returns false
oc.Properties.ServicePrincipalProfile = &api.ServicePrincipalProfile{}
},
clusterVersion: "4.10.0",
expected: deploymentData{
Image: operatorImageWithTag,
Version: operatorImageTag,
UsesWorkloadIdentity: false,
},
},
} {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
Expand Down Expand Up @@ -646,3 +681,50 @@ func TestGenerateOperatorIdentitySecret(t *testing.T) {
})
}
}

func TestTemplateManifests(t *testing.T) {
tests := []struct {
Name string
DeploymentData deploymentData
ExpectError bool
}{
{
Name: "service principal data",
DeploymentData: deploymentData{
Image: "someImage",
Version: "someVersion",
IsLocalDevelopment: false,
SupportsPodSecurityAdmission: false,
UsesWorkloadIdentity: false,
},
},
{
Name: "workload identity data",
DeploymentData: deploymentData{
Image: "someImage",
Version: "someVersion",
IsLocalDevelopment: false,
SupportsPodSecurityAdmission: false,
UsesWorkloadIdentity: true,
},
},
}

for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
actualBytes, err := templateManifests(test.DeploymentData)
if test.ExpectError == (err == nil) {
t.Errorf("templateManifests() %s: ExpectError: %t, actual error: %s\n", test.Name, test.ExpectError, err)
}

for _, fileBytes := range actualBytes {
var resource *kruntime.Object
err := yaml.Unmarshal(fileBytes, resource)

if test.ExpectError == (err == nil) {
t.Errorf("templateManifests() %s: ExpectError: %t, actual error: %s\n", test.Name, test.ExpectError, err)
}
}
})
}
}
23 changes: 22 additions & 1 deletion pkg/operator/deploy/staticresources/master/deployment.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,16 @@ spec:
secretKeyRef:
name: azure-cloud-credentials
key: azure_client_id
{{ if .UsesWorkloadIdentity }}
- name: AZURE_FEDERATED_TOKEN_FILE
value: "{{ .TokenVolumeMountPath }}"
{{ else }}
- name: AZURE_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: azure-cloud-credentials
key: azure_client_secret
{{ end }}
- name: AZURE_TENANT_ID
valueFrom:
secretKeyRef:
Expand All @@ -63,6 +68,12 @@ spec:
- ALL
runAsNonRoot: true
{{ end }}
{{ if .UsesWorkloadIdentity }}
volumeMounts:
- mountPath: "{{ .TokenVolumeMountPath }}"
name: bound-sa-token
readOnly: true
{{ end }}
nodeSelector:
node-role.kubernetes.io/master: ""
{{ if .SupportsPodSecurityAdmission }}
Expand All @@ -79,4 +90,14 @@ spec:
operator: Exists
- effect: NoSchedule
operator: Exists

{{ if .UsesWorkloadIdentity }}
volumes:
- name: bound-sa-token
projected:
defaultMode: 420
sources:
- serviceAccountToken:
audience: openshift
expirationSeconds: 3600
path: token
{{ end }}

0 comments on commit 1c88479

Please sign in to comment.