Skip to content

Commit 9af0b53

Browse files
Merge pull request #2054 from dmvolod/issue-1522
Set labels for the operator Deployment created via the ClusterServiceVersion
2 parents 7823ebe + 31e145a commit 9af0b53

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

18031876
// Create dependency first (CRD)

0 commit comments

Comments
 (0)