Skip to content

Commit 31e145a

Browse files
committed
Set labels for the operator Deployment created via the ClusterServiceVersion
Signed-off-by: Dmitry Volodin <[email protected]>
1 parent 76d665c commit 31e145a

File tree

4 files changed

+129
-11
lines changed

4 files changed

+129
-11
lines changed

doc/design/building-your-csv.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,13 +314,18 @@ Ensure that the `serviceAccountName` used in the `deployment` spec matches one o
314314

315315
Multiple Roles should be described to reduce the scope of any actions needed containers that the Operator may run on the cluster. For example, if you have a component that generates a TLS Secret upon start up, a Role that allows `create` but not `list` on Secrets is more secure than using a single all-powerful Service Account.
316316

317+
It is also possible to set custom labels for Deployment in addition to the system labels set by the OLM. This labels should be present in the `labels` fields of the `deployments` section.
318+
317319
Here’s a full example:
318320

319321
```yaml
320322
install:
321323
spec:
322324
deployments:
323325
- name: example-operator
326+
labels:
327+
application: example-operator
328+
technology: general
324329
spec:
325330
replicas: 1
326331
selector:

pkg/controller/install/deployment.go

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import (
88
appsv1 "k8s.io/api/apps/v1"
99
corev1 "k8s.io/api/core/v1"
1010
k8serrors "k8s.io/apimachinery/pkg/api/errors"
11+
k8slabels "k8s.io/apimachinery/pkg/labels"
1112
"k8s.io/apimachinery/pkg/util/rand"
13+
"k8s.io/utils/pointer"
1214

1315
"github.com/operator-framework/api/pkg/operators/v1alpha1"
1416
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/wrappers"
@@ -87,7 +89,7 @@ func NewStrategyDeploymentInstaller(strategyClient wrappers.InstallStrategyDeplo
8789

8890
func (i *StrategyDeploymentInstaller) installDeployments(deps []v1alpha1.StrategyDeploymentSpec) error {
8991
for _, d := range deps {
90-
deployment, _, err := i.deploymentForSpec(d.Name, d.Spec)
92+
deployment, _, err := i.deploymentForSpec(d.Name, d.Spec, d.Label)
9193
if err != nil {
9294
return err
9395
}
@@ -96,14 +98,14 @@ func (i *StrategyDeploymentInstaller) installDeployments(deps []v1alpha1.Strateg
9698
return err
9799
}
98100

99-
if err := i.createOrUpdateCertResourcesForDeployment(deployment.GetName()); err != nil {
101+
if err := i.createOrUpdateCertResourcesForDeployment(); err != nil {
100102
return err
101103
}
102104
}
103105
return nil
104106
}
105107

106-
func (i *StrategyDeploymentInstaller) createOrUpdateCertResourcesForDeployment(deploymentName string) error {
108+
func (i *StrategyDeploymentInstaller) createOrUpdateCertResourcesForDeployment() error {
107109
for _, desc := range i.getCertResources() {
108110
switch d := desc.(type) {
109111
case *apiServiceDescriptionsWithCAPEM:
@@ -123,13 +125,13 @@ func (i *StrategyDeploymentInstaller) createOrUpdateCertResourcesForDeployment(d
123125
return err
124126
}
125127
default:
126-
return fmt.Errorf("Unsupported CA Resource")
128+
return fmt.Errorf("unsupported CA Resource")
127129
}
128130
}
129131
return nil
130132
}
131133

132-
func (i *StrategyDeploymentInstaller) deploymentForSpec(name string, spec appsv1.DeploymentSpec) (deployment *appsv1.Deployment, hash string, err error) {
134+
func (i *StrategyDeploymentInstaller) deploymentForSpec(name string, spec appsv1.DeploymentSpec, specLabels k8slabels.Set) (deployment *appsv1.Deployment, hash string, err error) {
133135
dep := &appsv1.Deployment{Spec: spec}
134136
dep.SetName(name)
135137
dep.SetNamespace(i.owner.GetNamespace())
@@ -144,6 +146,9 @@ func (i *StrategyDeploymentInstaller) deploymentForSpec(name string, spec appsv1
144146
}
145147
dep.Spec.Template.SetAnnotations(annotations)
146148

149+
// Set custom labels before CSV owner labels
150+
dep.SetLabels(specLabels)
151+
147152
ownerutil.AddNonBlockingOwner(dep, i.owner)
148153
ownerutil.AddOwnerLabelsForKind(dep, i.owner, v1alpha1.ClusterServiceVersionKind)
149154

@@ -153,17 +158,19 @@ func (i *StrategyDeploymentInstaller) deploymentForSpec(name string, spec appsv1
153158
}
154159

155160
podSpec := &dep.Spec.Template.Spec
156-
inject.InjectEnvIntoDeployment(podSpec, []corev1.EnvVar{{
161+
if injectErr := inject.InjectEnvIntoDeployment(podSpec, []corev1.EnvVar{{
157162
Name: "OPERATOR_CONDITION_NAME",
158163
Value: i.owner.GetName(),
159-
}})
164+
}}); injectErr != nil {
165+
err = injectErr
166+
return
167+
}
160168

161169
// OLM does not support Rollbacks.
162170
// By default, each deployment created by OLM could spawn up to 10 replicaSets.
163171
// By setting the deployments revisionHistoryLimit to 1, OLM will only create up
164172
// to 2 ReplicaSets per deployment it manages, saving memory.
165-
revisionHistoryLimit := int32(1)
166-
dep.Spec.RevisionHistoryLimit = &revisionHistoryLimit
173+
dep.Spec.RevisionHistoryLimit = pointer.Int32Ptr(1)
167174

168175
hash = HashDeploymentSpec(dep.Spec)
169176
dep.Labels[DeploymentSpecHashLabelKey] = hash
@@ -286,7 +293,7 @@ func (i *StrategyDeploymentInstaller) checkForDeployments(deploymentSpecs []v1al
286293
return StrategyError{Reason: StrategyErrDeploymentUpdated, Message: fmt.Sprintf("deployment doesn't have a spec hash, update it")}
287294
}
288295

289-
_, calculatedDeploymentHash, err := i.deploymentForSpec(spec.Name, spec.Spec)
296+
_, calculatedDeploymentHash, err := i.deploymentForSpec(spec.Name, spec.Spec, labels)
290297
if err != nil {
291298
return StrategyError{Reason: StrategyErrDeploymentUpdated, Message: fmt.Sprintf("couldn't calculate deployment spec hash: %v", err)}
292299
}

pkg/controller/install/deployment_test.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,17 @@ import (
44
"fmt"
55
"testing"
66

7-
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubernetes/pkg/util/labels"
87
"github.com/stretchr/testify/assert"
98
"github.com/stretchr/testify/require"
109
appsv1 "k8s.io/api/apps/v1"
1110
corev1 "k8s.io/api/core/v1"
1211
rbacv1 "k8s.io/api/rbac/v1"
1312
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13+
k8slabels "k8s.io/apimachinery/pkg/labels"
1414

1515
"github.com/operator-framework/api/pkg/operators/v1alpha1"
1616
clientfakes "github.com/operator-framework/operator-lifecycle-manager/pkg/api/wrappers/wrappersfakes"
17+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubernetes/pkg/util/labels"
1718
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
1819
)
1920

@@ -142,6 +143,11 @@ func TestInstallStrategyDeploymentInstallDeployments(t *testing.T) {
142143
Name: "test-deployment-3",
143144
Spec: appsv1.DeploymentSpec{},
144145
},
146+
{
147+
Name: "test-deployment-4",
148+
Spec: appsv1.DeploymentSpec{},
149+
Label: k8slabels.Set{"custom-label": "custom-label-value"},
150+
},
145151
},
146152
},
147153
setup: setup{
@@ -228,6 +234,29 @@ func TestInstallStrategyDeploymentInstallDeployments(t *testing.T) {
228234
},
229235
returnError: nil,
230236
},
237+
{
238+
expectedDeployment: appsv1.Deployment{
239+
ObjectMeta: metav1.ObjectMeta{
240+
Name: "test-deployment-4",
241+
Namespace: mockOwner.GetNamespace(),
242+
OwnerReferences: mockOwnerRefs,
243+
Labels: map[string]string{
244+
"olm.owner": mockOwner.GetName(),
245+
"olm.owner.namespace": mockOwner.GetNamespace(),
246+
"custom-label": "custom-label-value",
247+
},
248+
},
249+
Spec: appsv1.DeploymentSpec{
250+
RevisionHistoryLimit: &expectedRevisionHistoryLimit,
251+
Template: corev1.PodTemplateSpec{
252+
ObjectMeta: metav1.ObjectMeta{
253+
Annotations: map[string]string{},
254+
},
255+
},
256+
},
257+
},
258+
returnError: nil,
259+
},
231260
},
232261
output: nil,
233262
},
@@ -243,6 +272,10 @@ func TestInstallStrategyDeploymentInstallDeployments(t *testing.T) {
243272
dep := fakeClient.CreateOrUpdateDeploymentArgsForCall(i)
244273
expectedDeployment.Spec.Template.Annotations = map[string]string{}
245274
require.Equal(t, expectedDeployment.OwnerReferences, dep.OwnerReferences)
275+
for labelKey, labelValue := range expectedDeployment.Labels {
276+
require.Contains(t, dep.GetLabels(), labelKey)
277+
require.Equal(t, dep.Labels[labelKey], labelValue)
278+
}
246279
require.Equal(t, expectedDeployment.Spec.RevisionHistoryLimit, dep.Spec.RevisionHistoryLimit)
247280
}(i, m.expectedDeployment)
248281
}

test/e2e/csv_e2e_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"k8s.io/apimachinery/pkg/api/errors"
1919
k8serrors "k8s.io/apimachinery/pkg/api/errors"
2020
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
k8slabels "k8s.io/apimachinery/pkg/labels"
2122
"k8s.io/apimachinery/pkg/runtime"
2223
"k8s.io/apimachinery/pkg/util/intstr"
2324
"k8s.io/apimachinery/pkg/util/wait"
@@ -1799,6 +1800,78 @@ var _ = Describe("ClusterServiceVersion", func() {
17991800
Expect(annotations["foo2"]).Should(Equal("bar2"))
18001801
Expect(annotations["foo3"]).Should(Equal("bar3"))
18011802
})
1803+
It("Set labels for the Deployment created via the ClusterServiceVersion", func() {
1804+
// Create a StrategyDetailsDeployment that defines labels for Deployment inside
1805+
nginxName := genName("nginx-")
1806+
strategy := v1alpha1.StrategyDetailsDeployment{
1807+
DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{
1808+
{
1809+
Name: genName("dep-"),
1810+
Spec: newNginxDeployment(nginxName),
1811+
Label: k8slabels.Set{
1812+
"application": "nginx",
1813+
"application.type": "proxy",
1814+
},
1815+
},
1816+
},
1817+
}
1818+
1819+
csv := v1alpha1.ClusterServiceVersion{
1820+
TypeMeta: metav1.TypeMeta{
1821+
Kind: v1alpha1.ClusterServiceVersionKind,
1822+
APIVersion: v1alpha1.ClusterServiceVersionAPIVersion,
1823+
},
1824+
ObjectMeta: metav1.ObjectMeta{
1825+
Name: genName("csv"),
1826+
},
1827+
Spec: v1alpha1.ClusterServiceVersionSpec{
1828+
MinKubeVersion: "0.0.0",
1829+
InstallModes: []v1alpha1.InstallMode{
1830+
{
1831+
Type: v1alpha1.InstallModeTypeOwnNamespace,
1832+
Supported: true,
1833+
},
1834+
{
1835+
Type: v1alpha1.InstallModeTypeSingleNamespace,
1836+
Supported: true,
1837+
},
1838+
{
1839+
Type: v1alpha1.InstallModeTypeMultiNamespace,
1840+
Supported: true,
1841+
},
1842+
{
1843+
Type: v1alpha1.InstallModeTypeAllNamespaces,
1844+
Supported: true,
1845+
},
1846+
},
1847+
InstallStrategy: v1alpha1.NamedInstallStrategy{
1848+
StrategyName: v1alpha1.InstallStrategyNameDeployment,
1849+
StrategySpec: strategy,
1850+
},
1851+
},
1852+
}
1853+
1854+
// Create the CSV and make sure to clean it up
1855+
cleanupCSV, err := createCSV(c, crc, csv, testNamespace, false, false)
1856+
Expect(err).ShouldNot(HaveOccurred())
1857+
defer cleanupCSV()
1858+
1859+
// Wait for current CSV to succeed
1860+
_, err = fetchCSV(crc, csv.Name, testNamespace, csvSucceededChecker)
1861+
Expect(err).ShouldNot(HaveOccurred())
1862+
1863+
// Should have created deployment
1864+
dep, err := c.GetDeployment(testNamespace, strategy.DeploymentSpecs[0].Name)
1865+
Expect(err).ShouldNot(HaveOccurred())
1866+
Expect(dep).ShouldNot(BeNil())
1867+
1868+
// Make sure that the deployment labels are correct
1869+
labels := dep.GetLabels()
1870+
Expect(labels["olm.owner"]).Should(Equal(csv.GetName()))
1871+
Expect(labels["olm.owner.namespace"]).Should(Equal(testNamespace))
1872+
Expect(labels["application"]).Should(Equal("nginx"))
1873+
Expect(labels["application.type"]).Should(Equal("proxy"))
1874+
})
18021875
It("update same deployment name", func() {
18031876

18041877
// Create dependency first (CRD)

0 commit comments

Comments
 (0)