Skip to content
This repository was archived by the owner on Oct 14, 2020. It is now read-only.

Commit d75694d

Browse files
authored
feat: Configure namespaces and install modes (#34)
Signed-off-by: Daniel Pacak <[email protected]>
1 parent 8a5d85e commit d75694d

File tree

5 files changed

+109
-33
lines changed

5 files changed

+109
-33
lines changed

README.md

+4-7
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@ a Kubernetes cluster - for example, initiating a vulnerability scan when a new p
1717
```
1818
1. Define Custom Security Resources used by Starboard:
1919
```
20-
$ kubectl apply -f https://raw.githubusercontent.com/aquasecurity/starboard/master/kube/crd/vulnerabilityreports-crd.yaml \
21-
-f https://raw.githubusercontent.com/aquasecurity/starboard/master/kube/crd/configauditreports-crd.yaml \
22-
-f https://raw.githubusercontent.com/aquasecurity/starboard/master/kube/crd/ciskubebenchreports-crd.yaml \
23-
-f https://raw.githubusercontent.com/aquasecurity/starboard/master/kube/crd/kubehunterreports-crd.yaml
20+
$ kubectl apply -f https://raw.githubusercontent.com/aquasecurity/starboard/master/kube/crd/vulnerabilityreports-crd.yaml
2421
```
2522
2. Create the `starboard-operator` Namespace:
2623
```
@@ -44,11 +41,11 @@ a Kubernetes cluster - for example, initiating a vulnerability scan when a new p
4441

4542
| Name | Default | Description |
4643
|-----------------------------------------|----------------------|-------------|
47-
| `OPERATOR_STARBOARD_NAMESPACE` | `starboard-operator` | The default namespace for Starboard |
48-
| `OPERATOR_SUPERVISED_NAMESPACE` | `default` | The namespace watched by the operator |
44+
| `OPERATOR_NAMESPACE` | `` | The namespace the operator is running in. |
45+
| `OPERATOR_TARGET_NAMESPACE` | `` | The namespace the operator should be watching for changes. This can be a comma separated list of names to watch multiple namespaces (e.g. `ns1,ns2`). |
4946
| `OPERATOR_SCAN_JOB_TIMEOUT` | `5m` | The length of time to wait before giving up on a scan job |
5047
| `OPERATOR_SCANNER_TRIVY_ENABLED` | `true` | The flag to enable Trivy vulnerability scanner |
51-
| `OPERATOR_SCANNER_TRIVY_VERSION` | `0.11.0` | The version of Trivy to be used |
48+
| `OPERATOR_SCANNER_TRIVY_VERSION` | `0.11.0` | The version of Trivy to be used |
5249
| `OPERATOR_SCANNER_AQUA_CSP_ENABLED` | `false` | The flag to enable Aqua CSP vulnerability scanner |
5350
| `OPERATOR_SCANNER_AQUA_CSP_VERSION` | `5.0` | The version of Aqua CSP scannercli container image to be used |
5451

cmd/operator/main.go

+60-17
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66

77
"github.com/aquasecurity/starboard-operator/pkg/logs"
88
"k8s.io/client-go/kubernetes"
9+
"sigs.k8s.io/controller-runtime/pkg/cache"
10+
"sigs.k8s.io/controller-runtime/pkg/manager"
911

1012
"github.com/aquasecurity/starboard-operator/pkg/aqua"
1113

@@ -46,7 +48,7 @@ var (
4648

4749
var (
4850
scheme = runtime.NewScheme()
49-
setupLog = ctrl.Log.WithName("setup")
51+
setupLog = logf.Log.WithName("starboard-operator.main")
5052
)
5153

5254
func init() {
@@ -59,36 +61,77 @@ func init() {
5961
func main() {
6062
logf.SetLogger(zap.New())
6163

62-
ctrl.SetLogger(logf.Log.WithName("starboard-operator"))
6364
if err := run(); err != nil {
6465
setupLog.Error(err, "Unable to run manager")
6566
}
6667
}
6768

6869
func run() error {
69-
config, err := etc.GetConfig()
70+
setupLog.Info("Starting operator", "version", versionInfo)
71+
config, err := etc.GetOperatorConfig()
7072
if err != nil {
71-
return err
73+
return fmt.Errorf("getting operator config: %w", err)
7274
}
7375

74-
kubernetesConfig := ctrl.GetConfigOrDie()
75-
// TODO Do not use this client unless absolutely necessary. We should rely on the client constructed by the ctrl.NewManager()
76-
kubernetesClientset, err := kubernetes.NewForConfig(kubernetesConfig)
76+
// Validate configured namespaces
77+
operatorNamespace, err := config.GetOperatorNamespace()
7778
if err != nil {
78-
return err
79+
return fmt.Errorf("getting operator namespace: %w", err)
7980
}
8081

81-
scanner, err := getEnabledScanner(config)
82+
targetNamespaces, err := config.GetTargetNamespaces()
8283
if err != nil {
83-
return err
84+
return fmt.Errorf("getting target namespaces: %w", err)
85+
}
86+
87+
setupLog.Info("Resolving multitenancy support",
88+
"operatorNamespace", operatorNamespace,
89+
"targetNamespaces", targetNamespaces)
90+
91+
mode, err := etc.ResolveInstallMode(operatorNamespace, targetNamespaces)
92+
if err != nil {
93+
return fmt.Errorf("resolving install mode: %w", err)
8494
}
95+
setupLog.Info("Resolving install mode", "mode", mode)
8596

86-
mgr, err := ctrl.NewManager(kubernetesConfig, ctrl.Options{
97+
// Set the default manager options.
98+
options := manager.Options{
8799
Scheme: scheme,
88-
})
100+
}
101+
102+
if len(targetNamespaces) == 1 {
103+
// Add support for OwnNamespace and SingleNamespace set in STARBOARD_TARGET_NAMESPACE (e.g. ns1).
104+
setupLog.Info("Constructing single-namespaced cache", "namespace", targetNamespaces[0])
105+
options.Namespace = targetNamespaces[0]
106+
} else {
107+
// Add support for MultiNamespace set in STARBOARD_TARGET_NAMESPACE (e.g. ns1,ns2).
108+
// Note that we may face performance issues when using this with a high number of namespaces.
109+
// More: https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/cache#MultiNamespacedCacheBuilder
110+
setupLog.Info("Constructing multi-namespaced cache", "namespaces", targetNamespaces)
111+
options.Namespace = ""
112+
options.NewCache = cache.MultiNamespacedCacheBuilder(targetNamespaces)
113+
}
89114

115+
kubernetesConfig, err := ctrl.GetConfig()
90116
if err != nil {
91-
return fmt.Errorf("unable to start manager: %w", err)
117+
return fmt.Errorf("getting kube client config: %w", err)
118+
}
119+
120+
// The only reason we're using kubernetes.Clientset is that we need it to read Pod logs,
121+
// which is not supported by the client returned by the ctrl.Manager.
122+
kubernetesClientset, err := kubernetes.NewForConfig(kubernetesConfig)
123+
if err != nil {
124+
return fmt.Errorf("constructing kube client: %w", err)
125+
}
126+
127+
mgr, err := ctrl.NewManager(kubernetesConfig, options)
128+
if err != nil {
129+
return fmt.Errorf("constructing controllers manager: %w", err)
130+
}
131+
132+
scanner, err := getEnabledScanner(config)
133+
if err != nil {
134+
return err
92135
}
93136

94137
store := reports.NewStore(mgr.GetClient(), scheme)
@@ -98,7 +141,7 @@ func run() error {
98141
Client: mgr.GetClient(),
99142
Store: store,
100143
Scanner: scanner,
101-
Log: ctrl.Log.WithName("controllers").WithName("pod"),
144+
Log: ctrl.Log.WithName("controller").WithName("pods"),
102145
Scheme: mgr.GetScheme(),
103146
}).SetupWithManager(mgr); err != nil {
104147
return fmt.Errorf("unable to create pod controller: %w", err)
@@ -110,15 +153,15 @@ func run() error {
110153
Client: mgr.GetClient(),
111154
Store: store,
112155
Scanner: scanner,
113-
Log: ctrl.Log.WithName("controllers").WithName("job"),
156+
Log: ctrl.Log.WithName("controller").WithName("scan-jobs"),
114157
Scheme: mgr.GetScheme(),
115158
}).SetupWithManager(mgr); err != nil {
116159
return fmt.Errorf("unable to create job controller: %w", err)
117160
}
118161

119-
setupLog.Info("starting manager")
162+
setupLog.Info("Starting controllers manager")
120163
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
121-
return fmt.Errorf("problem running manager: %w", err)
164+
return fmt.Errorf("starting controllers manager: %w", err)
122165
}
123166

124167
return nil

pkg/controllers/job_controller.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type JobReconciler struct {
3737
func (r *JobReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
3838
ctx := context.Background()
3939
log := r.Log.WithValues("job", req.NamespacedName)
40-
if req.Namespace != r.Config.StarboardNamespace {
40+
if req.Namespace != r.Config.Namespace {
4141
return ctrl.Result{}, nil
4242
}
4343

pkg/controllers/pod_controller.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ type PodReconciler struct {
4545
func (r *PodReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
4646
ctx := context.Background()
4747

48-
if r.Config.StarboardNamespace == req.Namespace {
48+
if r.Config.Namespace == req.Namespace {
4949
return ctrl.Result{}, nil
5050
}
5151

@@ -120,7 +120,7 @@ func (r *PodReconciler) ensureScanJob(ctx context.Context, owner kube.Object, p
120120
kube.LabelResourceNamespace: p.Namespace,
121121
kube.LabelResourceKind: string(owner.Kind),
122122
kube.LabelResourceName: owner.Name,
123-
}, client.InNamespace(r.Config.StarboardNamespace))
123+
}, client.InNamespace(r.Config.Namespace))
124124
if err != nil {
125125
return err
126126
}
@@ -131,7 +131,7 @@ func (r *PodReconciler) ensureScanJob(ctx context.Context, owner kube.Object, p
131131
}
132132

133133
scanJob, secret, err := r.Scanner.NewScanJob(owner, p.Spec, scanner.Options{
134-
Namespace: r.Config.StarboardNamespace,
134+
Namespace: r.Config.Namespace,
135135
ServiceAccountName: r.Config.ServiceAccount,
136136
ImageCredentials: make(map[string]docker.Auth),
137137
ScanJobTimeout: r.Config.ScanJobTimeout,

pkg/etc/config.go

+41-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package etc
22

33
import (
4+
"errors"
5+
"fmt"
6+
"strings"
47
"time"
58

69
"github.com/caarlos0/env/v6"
@@ -19,10 +22,10 @@ type Config struct {
1922
}
2023

2124
type Operator struct {
22-
StarboardNamespace string `env:"OPERATOR_STARBOARD_NAMESPACE" envDefault:"starboard-operator"`
23-
SupervisedNamespace string `env:"OPERATOR_SUPERVISED_NAMESPACE" envDefault:"default"`
24-
ServiceAccount string `env:"OPERATOR_SERVICE_ACCOUNT" envDefault:"starboard-operator"`
25-
ScanJobTimeout time.Duration `env:"OPERATOR_SCAN_JOB_TIMEOUT" envDefault:"5m"`
25+
Namespace string `env:"OPERATOR_NAMESPACE"`
26+
TargetNamespace string `env:"OPERATOR_TARGET_NAMESPACE"`
27+
ServiceAccount string `env:"OPERATOR_SERVICE_ACCOUNT" envDefault:"starboard-operator"`
28+
ScanJobTimeout time.Duration `env:"OPERATOR_SCAN_JOB_TIMEOUT" envDefault:"5m"`
2629
}
2730

2831
type ScannerTrivy struct {
@@ -38,8 +41,41 @@ type ScannerAquaCSP struct {
3841
Password string `env:"OPERATOR_SCANNER_AQUA_CSP_PASSWORD"`
3942
}
4043

41-
func GetConfig() (Config, error) {
44+
func GetOperatorConfig() (Config, error) {
4245
var config Config
4346
err := env.Parse(&config)
4447
return config, err
4548
}
49+
50+
// GetOperatorNamespace returns the namespace the operator should be running in.
51+
func (c Config) GetOperatorNamespace() (string, error) {
52+
namespace := c.Operator.Namespace
53+
if namespace != "" {
54+
return namespace, nil
55+
}
56+
return "", fmt.Errorf("%s must be set", "OPERATOR_NAMESPACE")
57+
}
58+
59+
// GetTargetNamespaces returns namespaces the operator should be watching for changes.
60+
func (c Config) GetTargetNamespaces() ([]string, error) {
61+
namespace := c.Operator.TargetNamespace
62+
if namespace != "" {
63+
return strings.Split(namespace, ","), nil
64+
}
65+
return nil, fmt.Errorf("%s must be set", "OPERATOR_TARGET_NAMESPACE")
66+
}
67+
68+
// ResolveInstallMode resolves install mode defined by Operator Lifecycle Manager.
69+
// We do that for debugging purposes.
70+
func ResolveInstallMode(operatorNamespace string, targetNamespaces []string) (string, error) {
71+
if len(targetNamespaces) == 1 && operatorNamespace == targetNamespaces[0] {
72+
return "OwnNamespace", nil
73+
}
74+
if len(targetNamespaces) == 1 && operatorNamespace != targetNamespaces[0] {
75+
return "SingleNamespace", nil
76+
}
77+
if len(targetNamespaces) > 1 {
78+
return "MultiNamespace", nil
79+
}
80+
return "", errors.New("unsupported install mode")
81+
}

0 commit comments

Comments
 (0)