Skip to content

Commit 1f42f85

Browse files
authored
internal/pkg/scaffold/olm-catalog: use olm.targetNamespaces ann… (#1709)
* internal/pkg/scaffold/olm-catalog/csv_updaters.go: use olm.targetNamespaces annotation in WATCH_NAMESPACE, and add unit tests * CHANGELOG.md: add CSV WATCH_NAMESPACE update
1 parent 99b7b09 commit 1f42f85

File tree

6 files changed

+106
-3
lines changed

6 files changed

+106
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
### Bug Fixes
1212

1313
- Fixes header file content validation when the content contains empty lines or centered text. ([#1544](https://github.com/operator-framework/operator-sdk/pull/1544))
14+
- Generated CSV's that include a deployment install strategy will be checked for a reference to `metadata.annotations['olm.targetNamespaces']`, and if one is not found a reference will be added to the `WATCH_NAMESPACE` env var for all containers in the deployment. This is a bug because any other value that references the CSV's namespace is incorrect. ([#1396](https://github.com/operator-framework/operator-sdk/pull/1396))
1415

1516
## v0.8.1
1617

internal/pkg/scaffold/olm-catalog/csv_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ import (
2626
"github.com/operator-framework/operator-sdk/internal/pkg/scaffold/input"
2727
testutil "github.com/operator-framework/operator-sdk/internal/pkg/scaffold/internal/testutil"
2828
"github.com/operator-framework/operator-sdk/internal/util/diffutil"
29+
"github.com/operator-framework/operator-sdk/pkg/k8sutil"
2930

3031
"github.com/coreos/go-semver/semver"
3132
"github.com/ghodss/yaml"
3233
olmapiv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
3334
olminstall "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
3435
"github.com/spf13/afero"
36+
appsv1 "k8s.io/api/apps/v1"
3537
)
3638

3739
const testDataDir = "testdata"
@@ -185,3 +187,45 @@ func TestGetDisplayName(t *testing.T) {
185187
}
186188
}
187189
}
190+
191+
func TestSetAndCheckOLMNamespaces(t *testing.T) {
192+
depBytes, err := ioutil.ReadFile(filepath.Join(testDeployDir, "operator.yaml"))
193+
if err != nil {
194+
t.Fatalf("Failed to read Deployment bytes: %v", err)
195+
}
196+
197+
// The test operator.yaml doesn't have "olm.targetNamespaces", so first
198+
// check that depHasOLMNamespaces() returns false.
199+
dep := &appsv1.Deployment{}
200+
if err := yaml.Unmarshal(depBytes, dep); err != nil {
201+
t.Fatalf("Failed to unmarshal Deployment bytes: %v", err)
202+
}
203+
if depHasOLMNamespaces(dep) {
204+
t.Error("Expected depHasOLMNamespaces to return false, got true")
205+
}
206+
207+
// Insert "olm.targetNamespaces" into WATCH_NAMESPACE and check that
208+
// depHasOLMNamespaces() returns true.
209+
setWatchNamespacesEnv(dep)
210+
if !depHasOLMNamespaces(dep) {
211+
t.Error("Expected depHasOLMNamespaces to return true, got false")
212+
}
213+
214+
// Overwrite WATCH_NAMESPACE and check that depHasOLMNamespaces() returns
215+
// false.
216+
overwriteContainerEnvVar(dep, k8sutil.WatchNamespaceEnvVar, newEnvVar("FOO", "bar"))
217+
if depHasOLMNamespaces(dep) {
218+
t.Error("Expected depHasOLMNamespaces to return false, got true")
219+
}
220+
221+
// Insert "olm.targetNamespaces" elsewhere in the deployment pod spec
222+
// and check that depHasOLMNamespaces() returns true.
223+
dep = &appsv1.Deployment{}
224+
if err := yaml.Unmarshal(depBytes, dep); err != nil {
225+
t.Fatalf("Failed to unmarshal Deployment bytes: %v", err)
226+
}
227+
dep.Spec.Template.ObjectMeta.Labels["namespace"] = olmTNMeta
228+
if !depHasOLMNamespaces(dep) {
229+
t.Error("Expected depHasOLMNamespaces to return true, got false")
230+
}
231+
}

internal/pkg/scaffold/olm-catalog/csv_updaters.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,19 @@
1515
package catalog
1616

1717
import (
18+
"bytes"
1819
"encoding/json"
1920
"fmt"
2021
"strings"
2122

23+
"github.com/operator-framework/operator-sdk/pkg/k8sutil"
24+
2225
"github.com/ghodss/yaml"
2326
olmapiv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
2427
olminstall "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
28+
log "github.com/sirupsen/logrus"
2529
appsv1 "k8s.io/api/apps/v1"
30+
corev1 "k8s.io/api/core/v1"
2631
rbacv1 "k8s.io/api/rbac/v1"
2732
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
2833
)
@@ -124,11 +129,23 @@ func (store *updaterStore) AddClusterRole(yamlDoc []byte) error {
124129
return nil
125130
}
126131

132+
const olmTNMeta = "metadata.annotations['olm.targetNamespaces']"
133+
127134
func (store *updaterStore) AddDeploymentSpec(yamlDoc []byte) error {
128135
dep := &appsv1.Deployment{}
129136
if err := yaml.Unmarshal(yamlDoc, dep); err != nil {
130137
return err
131138
}
139+
140+
setWatchNamespacesEnv(dep)
141+
// Make sure "olm.targetNamespaces" is referenced somewhere in dep,
142+
// and emit a warning of not.
143+
if !depHasOLMNamespaces(dep) {
144+
log.Warnf(`No WATCH_NAMESPACE environment variable nor reference to "%s"`+
145+
` detected in operator Deployment. For OLM compatibility, your operator`+
146+
` MUST watch namespaces defined in "%s"`, olmTNMeta, olmTNMeta)
147+
}
148+
132149
depSpec := olminstall.StrategyDeploymentSpec{
133150
Name: dep.ObjectMeta.Name,
134151
Spec: dep.Spec,
@@ -138,6 +155,47 @@ func (store *updaterStore) AddDeploymentSpec(yamlDoc []byte) error {
138155
return nil
139156
}
140157

158+
// setWatchNamespacesEnv sets WATCH_NAMESPACE to olmTNString in dep if
159+
// WATCH_NAMESPACE exists in a pod spec container's env list.
160+
func setWatchNamespacesEnv(dep *appsv1.Deployment) {
161+
overwriteContainerEnvVar(dep, k8sutil.WatchNamespaceEnvVar, newEnvVar(k8sutil.WatchNamespaceEnvVar, olmTNMeta))
162+
}
163+
164+
func overwriteContainerEnvVar(dep *appsv1.Deployment, name string, ev corev1.EnvVar) {
165+
for _, c := range dep.Spec.Template.Spec.Containers {
166+
for i := 0; i < len(c.Env); i++ {
167+
if c.Env[i].Name == name {
168+
c.Env[i] = ev
169+
}
170+
}
171+
}
172+
}
173+
174+
func newEnvVar(name, fieldPath string) corev1.EnvVar {
175+
return corev1.EnvVar{
176+
Name: name,
177+
ValueFrom: &corev1.EnvVarSource{
178+
FieldRef: &corev1.ObjectFieldSelector{
179+
FieldPath: fieldPath,
180+
},
181+
},
182+
}
183+
}
184+
185+
// OLM places the set of target namespaces for the operator in
186+
// "metadata.annotations['olm.targetNamespaces']". This value should be
187+
// referenced in either:
188+
// - The Deployment's pod spec WATCH_NAMESPACE env variable.
189+
// - Some other Deployment pod spec field.
190+
func depHasOLMNamespaces(dep *appsv1.Deployment) bool {
191+
b, err := dep.Spec.Template.Marshal()
192+
if err != nil {
193+
// Something is wrong with the deployment manifest, not with CLI inputs.
194+
log.Fatalf("marshal Deployment spec: %v", err)
195+
}
196+
return bytes.Index(b, []byte(olmTNMeta)) != -1
197+
}
198+
141199
func (u *InstallStrategyUpdate) Apply(csv *olmapiv1alpha1.ClusterServiceVersion) (err error) {
142200
// Get install strategy from csv. Default to a deployment strategy if none found.
143201
var strat olminstall.Strategy

internal/pkg/scaffold/olm-catalog/testdata/deploy/olm-catalog/app-operator/0.1.0/app-operator.v0.1.0.clusterserviceversion.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ spec:
4848
- name: WATCH_NAMESPACE
4949
valueFrom:
5050
fieldRef:
51-
fieldPath: metadata.namespace
51+
fieldPath: metadata.annotations['olm.targetNamespaces']
5252
- name: OPERATOR_NAME
5353
value: app-operator
5454
image: quay.io/example-inc/operator:v0.1.0

test/test-framework/deploy/olm-catalog/memcached-operator/0.0.2/memcached-operator.v0.0.2.clusterserviceversion.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ spec:
8484
- name: WATCH_NAMESPACE
8585
valueFrom:
8686
fieldRef:
87-
fieldPath: metadata.namespace
87+
fieldPath: metadata.annotations['olm.targetNamespaces']
8888
- name: POD_NAME
8989
valueFrom:
9090
fieldRef:

test/test-framework/deploy/olm-catalog/memcached-operator/0.0.3/memcached-operator.v0.0.3.clusterserviceversion.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ spec:
7777
- name: WATCH_NAMESPACE
7878
valueFrom:
7979
fieldRef:
80-
fieldPath: metadata.namespace
80+
fieldPath: metadata.annotations['olm.targetNamespaces']
8181
- name: POD_NAME
8282
valueFrom:
8383
fieldRef:

0 commit comments

Comments
 (0)