Skip to content

Commit 5bb3d6d

Browse files
committed
Adds OLM Operator Plugin interface and CSV Namespace labeller plug-in
Signed-off-by: perdasilva <[email protected]> Upstream-repository: perdasilva Upstream-commit: baaafa10f8d9f5c3251bfccb207c20bb84cdbc80 Signed-off-by: perdasilva <[email protected]>
1 parent 1ecfafd commit 5bb3d6d

File tree

8 files changed

+860
-48
lines changed

8 files changed

+860
-48
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package olm
2+
3+
import (
4+
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/plugins"
5+
)
6+
7+
func init() {
8+
operatorPlugInFactoryFuncs = []plugins.OperatorPlugInFactoryFunc{
9+
// labels unlabeled non-payload openshift-* csv namespaces with
10+
// security.openshift.io/scc.podSecurityLabelSync: true
11+
plugins.NewCsvNamespaceLabelerPlugInfunc,
12+
}
13+
}

staging/operator-lifecycle-manager/pkg/controller/operators/olm/operator_plugin.go

Lines changed: 0 additions & 24 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
package plugins
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/operator-framework/api/pkg/operators/v1alpha1"
9+
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned"
10+
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/pruning"
11+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubestate"
12+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
13+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/queueinformer"
14+
"github.com/sirupsen/logrus"
15+
v1 "k8s.io/api/core/v1"
16+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17+
"k8s.io/apimachinery/pkg/util/sets"
18+
"k8s.io/client-go/informers"
19+
listerv1 "k8s.io/client-go/listers/core/v1"
20+
"k8s.io/client-go/tools/cache"
21+
"k8s.io/client-go/util/workqueue"
22+
)
23+
24+
const NamespaceLabelSyncerLabelKey = "security.openshift.io/scc.podSecurityLabelSync"
25+
26+
// systemNSSyncExemptions is the list of namespaces deployed by an OpenShift install
27+
// payload, as retrieved by listing the namespaces after a successful installation
28+
// IMPORTANT: The Namespace openshift-operators must be an exception to this rule
29+
// since it is used by OCP/OLM users to install their Operator bundle solutions.
30+
var systemNSSyncExemptions = sets.NewString(
31+
// kube-specific system namespaces
32+
"default",
33+
"kube-node-lease",
34+
"kube-public",
35+
"kube-system",
36+
37+
// openshift payload namespaces
38+
"openshift",
39+
"openshift-apiserver",
40+
"openshift-apiserver-operator",
41+
"openshift-authentication",
42+
"openshift-authentication-operator",
43+
"openshift-cloud-controller-manager",
44+
"openshift-cloud-controller-manager-operator",
45+
"openshift-cloud-credential-operator",
46+
"openshift-cloud-network-config-controller",
47+
"openshift-cluster-csi-drivers",
48+
"openshift-cluster-machine-approver",
49+
"openshift-cluster-node-tuning-operator",
50+
"openshift-cluster-samples-operator",
51+
"openshift-cluster-storage-operator",
52+
"openshift-cluster-version",
53+
"openshift-config",
54+
"openshift-config-managed",
55+
"openshift-config-operator",
56+
"openshift-console",
57+
"openshift-console-operator",
58+
"openshift-console-user-settings",
59+
"openshift-controller-manager",
60+
"openshift-controller-manager-operator",
61+
"openshift-dns",
62+
"openshift-dns-operator",
63+
"openshift-etcd",
64+
"openshift-etcd-operator",
65+
"openshift-host-network",
66+
"openshift-image-registry",
67+
"openshift-infra",
68+
"openshift-ingress",
69+
"openshift-ingress-canary",
70+
"openshift-ingress-operator",
71+
"openshift-insights",
72+
"openshift-kni-infra",
73+
"openshift-kube-apiserver",
74+
"openshift-kube-apiserver-operator",
75+
"openshift-kube-controller-manager",
76+
"openshift-kube-controller-manager-operator",
77+
"openshift-kube-scheduler",
78+
"openshift-kube-scheduler-operator",
79+
"openshift-kube-storage-version-migrator",
80+
"openshift-kube-storage-version-migrator-operator",
81+
"openshift-machine-api",
82+
"openshift-machine-config-operator",
83+
"openshift-marketplace",
84+
"openshift-monitoring",
85+
"openshift-multus",
86+
"openshift-network-diagnostics",
87+
"openshift-network-operator",
88+
"openshift-node",
89+
"openshift-nutanix-infra",
90+
"openshift-oauth-apiserver",
91+
"openshift-openstack-infra",
92+
"openshift-operator-lifecycle-manager",
93+
"openshift-ovirt-infra",
94+
"openshift-sdn",
95+
"openshift-service-ca",
96+
"openshift-service-ca-operator",
97+
"openshift-user-workload-monitoring",
98+
"openshift-vsphere-infra",
99+
)
100+
101+
// csvNamespaceLabelerPlugin is responsible for labeling non-payload openshift-* namespaces
102+
// with the label "security.openshift.io/scc.podSecurityLabelSync=true" so that the PSA Label Syncer
103+
// see https://github.com/openshift/cluster-policy-controller/blob/master/pkg/psalabelsyncer/podsecurity_label_sync_controller.go
104+
// can help ensure that the operator payloads in the namespace continue to work even if they don't yet respect the
105+
// upstream Pod Security Admission controller, which will become active in k8s 1.15.
106+
// see https://kubernetes.io/docs/concepts/security/pod-security-admission/
107+
// If a CSV is created or modified, this controller will look at the csv's namespace. If it is a non-payload namespace,
108+
// if the namespace name is prefixed with 'openshift-', and if the namespace does not contain the label (whatever
109+
// value it may be set to), it will add the "security.openshift.io/scc.podSecurityLabelSync=true" to the namespace.
110+
type csvNamespaceLabelerPlugin struct {
111+
namespaceListerMap map[string]listerv1.NamespaceLister
112+
kubeClient operatorclient.ClientInterface
113+
externalClient versioned.Interface
114+
logger *logrus.Logger
115+
}
116+
117+
func NewCsvNamespaceLabelerPlugInfunc(ctx context.Context, config OperatorConfig, hostOperator HostOperator) (OperatorPlugin, error) {
118+
119+
if hostOperator == nil {
120+
return nil, fmt.Errorf("cannot initialize plugin: operator undefined")
121+
}
122+
123+
plugin := &csvNamespaceLabelerPlugin{
124+
kubeClient: config.OperatorClient(),
125+
externalClient: config.ExternalClient(),
126+
logger: config.Logger(),
127+
namespaceListerMap: map[string]listerv1.NamespaceLister{},
128+
}
129+
130+
for _, namespace := range config.WatchedNamespaces() {
131+
132+
// create a namespace informer for namespaces that do not include
133+
// the label syncer label
134+
namespaceInformer := informers.NewSharedInformerFactoryWithOptions(
135+
plugin.kubeClient.KubernetesInterface(),
136+
config.ResyncPeriod()(),
137+
informers.WithNamespace(namespace),
138+
).Core().V1().Namespaces()
139+
140+
if err := hostOperator.RegisterInformer(namespaceInformer.Informer()); err != nil {
141+
return nil, err
142+
}
143+
plugin.namespaceListerMap[namespace] = namespaceInformer.Lister()
144+
145+
// create a new csv informer and prune status to reduce memory footprint
146+
csvNamespaceLabelerInformer := cache.NewSharedIndexInformer(
147+
pruning.NewListerWatcher(
148+
plugin.externalClient,
149+
namespace,
150+
func(opts *metav1.ListOptions) {
151+
opts.LabelSelector = fmt.Sprintf("!%s", v1alpha1.CopiedLabelKey)
152+
},
153+
pruning.PrunerFunc(func(csv *v1alpha1.ClusterServiceVersion) {
154+
*csv = v1alpha1.ClusterServiceVersion{
155+
TypeMeta: csv.TypeMeta,
156+
ObjectMeta: csv.ObjectMeta,
157+
}
158+
}),
159+
),
160+
&v1alpha1.ClusterServiceVersion{},
161+
config.ResyncPeriod()(),
162+
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
163+
)
164+
165+
csvNamespaceLabelerPluginQueue := workqueue.NewNamedRateLimitingQueue(
166+
workqueue.DefaultControllerRateLimiter(),
167+
fmt.Sprintf("%s/csv-ns-labeler-plugin", namespace),
168+
)
169+
csvNamespaceLabelerPluginQueueInformer, err := queueinformer.NewQueueInformer(
170+
ctx,
171+
queueinformer.WithInformer(csvNamespaceLabelerInformer),
172+
queueinformer.WithLogger(config.Logger()),
173+
queueinformer.WithQueue(csvNamespaceLabelerPluginQueue),
174+
queueinformer.WithIndexer(csvNamespaceLabelerInformer.GetIndexer()),
175+
queueinformer.WithSyncer(plugin),
176+
)
177+
if err != nil {
178+
return nil, err
179+
}
180+
if err := hostOperator.RegisterQueueInformer(csvNamespaceLabelerPluginQueueInformer); err != nil {
181+
return nil, err
182+
}
183+
}
184+
185+
return plugin, nil
186+
}
187+
188+
func (p *csvNamespaceLabelerPlugin) Shutdown() error {
189+
return nil
190+
}
191+
192+
func (p *csvNamespaceLabelerPlugin) Sync(ctx context.Context, event kubestate.ResourceEvent) error {
193+
// only act on csv added and updated events
194+
if event.Type() != kubestate.ResourceAdded && event.Type() != kubestate.ResourceUpdated {
195+
return nil
196+
}
197+
198+
csv, ok := event.Resource().(*v1alpha1.ClusterServiceVersion)
199+
if !ok {
200+
return fmt.Errorf("event resource is not a ClusterServiceVersion")
201+
}
202+
203+
// ignore copied csvs
204+
// informer should already be filtering these out - but just in case
205+
if csv.IsCopied() {
206+
return nil
207+
}
208+
209+
// ignore non-openshift-* and payload openshift-* namespaces
210+
if !strings.HasPrefix(csv.GetNamespace(), "openshift-") || systemNSSyncExemptions.Has(csv.GetNamespace()) {
211+
return nil
212+
}
213+
214+
namespace, err := p.getNamespace(csv.GetNamespace())
215+
if err != nil {
216+
return fmt.Errorf("error getting csv namespace (%s) for label sync'er labeling", csv.GetNamespace())
217+
}
218+
219+
// add label sync'er label if it does not exist
220+
if _, ok := namespace.GetLabels()[NamespaceLabelSyncerLabelKey]; !ok {
221+
nsCopy := namespace.DeepCopy()
222+
if nsCopy.GetLabels() == nil {
223+
nsCopy.SetLabels(map[string]string{})
224+
}
225+
nsCopy.GetLabels()[NamespaceLabelSyncerLabelKey] = "true"
226+
if _, err := p.kubeClient.KubernetesInterface().CoreV1().Namespaces().Update(ctx, nsCopy, metav1.UpdateOptions{}); err != nil {
227+
return fmt.Errorf("error updating csv namespace (%s) with label sync'er label", nsCopy.GetNamespace())
228+
}
229+
230+
if p.logger != nil {
231+
p.logger.Infof("applied %s=true label to namespace %s", NamespaceLabelSyncerLabelKey, nsCopy.GetNamespace())
232+
}
233+
}
234+
235+
return nil
236+
}
237+
238+
func (p *csvNamespaceLabelerPlugin) getNamespace(namespace string) (*v1.Namespace, error) {
239+
lister, ok := p.namespaceListerMap[namespace]
240+
if !ok {
241+
lister, ok = p.namespaceListerMap[metav1.NamespaceAll]
242+
if !ok {
243+
return nil, fmt.Errorf("no namespace lister found for namespace: %s", namespace)
244+
}
245+
}
246+
return lister.Get(namespace)
247+
}

0 commit comments

Comments
 (0)