Skip to content

Commit 7f167c2

Browse files
committed
operator-sdk scaffolding
1 parent 83820e2 commit 7f167c2

File tree

14 files changed

+1646
-0
lines changed

14 files changed

+1646
-0
lines changed

Diff for: build/Dockerfile

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM registry.access.redhat.com/ubi8/ubi-minimal:latest
2+
3+
ENV OPERATOR=/usr/local/bin/dv-operator \
4+
USER_UID=1001 \
5+
USER_NAME=dv-operator
6+
7+
# install operator binary
8+
COPY build/_output/bin/dv-operator ${OPERATOR}
9+
10+
COPY build/bin /usr/local/bin
11+
RUN /usr/local/bin/user_setup
12+
13+
ENTRYPOINT ["/usr/local/bin/entrypoint"]
14+
15+
USER ${USER_UID}

Diff for: build/_output/bin/dv-operator-local

42.6 MB
Binary file not shown.

Diff for: build/bin/entrypoint

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh -e
2+
3+
exec ${OPERATOR} $@

Diff for: build/bin/user_setup

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/sh
2+
set -x
3+
4+
# ensure $HOME exists and is accessible by group 0 (we don't know what the runtime UID will be)
5+
echo "${USER_NAME}:x:${USER_UID}:0:${USER_NAME} user:${HOME}:/sbin/nologin" >> /etc/passwd
6+
mkdir -p "${HOME}"
7+
chown "${USER_UID}:0" "${HOME}"
8+
chmod ug+rwx "${HOME}"
9+
10+
# no need for this script to remain in the image after running
11+
rm "$0"

Diff for: cmd/manager/main.go

+211
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"errors"
6+
"flag"
7+
"fmt"
8+
"os"
9+
"runtime"
10+
"strings"
11+
12+
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
13+
_ "k8s.io/client-go/plugin/pkg/client/auth"
14+
"k8s.io/client-go/rest"
15+
16+
"github.com/jmelis/dv-operator/pkg/apis"
17+
"github.com/jmelis/dv-operator/pkg/controller"
18+
"github.com/jmelis/dv-operator/version"
19+
20+
"github.com/operator-framework/operator-sdk/pkg/k8sutil"
21+
kubemetrics "github.com/operator-framework/operator-sdk/pkg/kube-metrics"
22+
"github.com/operator-framework/operator-sdk/pkg/leader"
23+
"github.com/operator-framework/operator-sdk/pkg/log/zap"
24+
"github.com/operator-framework/operator-sdk/pkg/metrics"
25+
sdkVersion "github.com/operator-framework/operator-sdk/version"
26+
"github.com/spf13/pflag"
27+
v1 "k8s.io/api/core/v1"
28+
"k8s.io/apimachinery/pkg/util/intstr"
29+
"sigs.k8s.io/controller-runtime/pkg/cache"
30+
"sigs.k8s.io/controller-runtime/pkg/client/config"
31+
logf "sigs.k8s.io/controller-runtime/pkg/log"
32+
"sigs.k8s.io/controller-runtime/pkg/manager"
33+
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
34+
)
35+
36+
// Change below variables to serve metrics on different host or port.
37+
var (
38+
metricsHost = "0.0.0.0"
39+
metricsPort int32 = 8383
40+
operatorMetricsPort int32 = 8686
41+
)
42+
var log = logf.Log.WithName("cmd")
43+
44+
func printVersion() {
45+
log.Info(fmt.Sprintf("Operator Version: %s", version.Version))
46+
log.Info(fmt.Sprintf("Go Version: %s", runtime.Version()))
47+
log.Info(fmt.Sprintf("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH))
48+
log.Info(fmt.Sprintf("Version of operator-sdk: %v", sdkVersion.Version))
49+
}
50+
51+
func main() {
52+
// Add the zap logger flag set to the CLI. The flag set must
53+
// be added before calling pflag.Parse().
54+
pflag.CommandLine.AddFlagSet(zap.FlagSet())
55+
56+
// Add flags registered by imported packages (e.g. glog and
57+
// controller-runtime)
58+
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
59+
60+
pflag.Parse()
61+
62+
// Use a zap logr.Logger implementation. If none of the zap
63+
// flags are configured (or if the zap flag set is not being
64+
// used), this defaults to a production zap logger.
65+
//
66+
// The logger instantiated here can be changed to any logger
67+
// implementing the logr.Logger interface. This logger will
68+
// be propagated through the whole operator, generating
69+
// uniform and structured logs.
70+
logf.SetLogger(zap.Logger())
71+
72+
printVersion()
73+
74+
namespace, err := k8sutil.GetWatchNamespace()
75+
if err != nil {
76+
log.Error(err, "Failed to get watch namespace")
77+
os.Exit(1)
78+
}
79+
80+
// Get a config to talk to the apiserver
81+
cfg, err := config.GetConfig()
82+
if err != nil {
83+
log.Error(err, "")
84+
os.Exit(1)
85+
}
86+
87+
ctx := context.TODO()
88+
// Become the leader before proceeding
89+
err = leader.Become(ctx, "dv-operator-lock")
90+
if err != nil {
91+
log.Error(err, "")
92+
os.Exit(1)
93+
}
94+
95+
// Set default manager options
96+
options := manager.Options{
97+
Namespace: namespace,
98+
MetricsBindAddress: fmt.Sprintf("%s:%d", metricsHost, metricsPort),
99+
}
100+
101+
// Add support for MultiNamespace set in WATCH_NAMESPACE (e.g ns1,ns2)
102+
// Note that this is not intended to be used for excluding namespaces, this is better done via a Predicate
103+
// Also note that you may face performance issues when using this with a high number of namespaces.
104+
// More Info: https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/cache#MultiNamespacedCacheBuilder
105+
if strings.Contains(namespace, ",") {
106+
options.Namespace = ""
107+
options.NewCache = cache.MultiNamespacedCacheBuilder(strings.Split(namespace, ","))
108+
}
109+
110+
// Create a new manager to provide shared dependencies and start components
111+
mgr, err := manager.New(cfg, options)
112+
if err != nil {
113+
log.Error(err, "")
114+
os.Exit(1)
115+
}
116+
117+
log.Info("Registering Components.")
118+
119+
// Setup Scheme for all resources
120+
if err := apis.AddToScheme(mgr.GetScheme()); err != nil {
121+
log.Error(err, "")
122+
os.Exit(1)
123+
}
124+
125+
// Setup all Controllers
126+
if err := controller.AddToManager(mgr); err != nil {
127+
log.Error(err, "")
128+
os.Exit(1)
129+
}
130+
131+
// Add the Metrics Service
132+
addMetrics(ctx, cfg)
133+
134+
log.Info("Starting the Cmd.")
135+
136+
// Start the Cmd
137+
if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
138+
log.Error(err, "Manager exited non-zero")
139+
os.Exit(1)
140+
}
141+
}
142+
143+
// addMetrics will create the Services and Service Monitors to allow the operator export the metrics by using
144+
// the Prometheus operator
145+
func addMetrics(ctx context.Context, cfg *rest.Config) {
146+
// Get the namespace the operator is currently deployed in.
147+
operatorNs, err := k8sutil.GetOperatorNamespace()
148+
if err != nil {
149+
if errors.Is(err, k8sutil.ErrRunLocal) {
150+
log.Info("Skipping CR metrics server creation; not running in a cluster.")
151+
return
152+
}
153+
}
154+
155+
if err := serveCRMetrics(cfg, operatorNs); err != nil {
156+
log.Info("Could not generate and serve custom resource metrics", "error", err.Error())
157+
}
158+
159+
// Add to the below struct any other metrics ports you want to expose.
160+
servicePorts := []v1.ServicePort{
161+
{Port: metricsPort, Name: metrics.OperatorPortName, Protocol: v1.ProtocolTCP, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: metricsPort}},
162+
{Port: operatorMetricsPort, Name: metrics.CRPortName, Protocol: v1.ProtocolTCP, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: operatorMetricsPort}},
163+
}
164+
165+
// Create Service object to expose the metrics port(s).
166+
service, err := metrics.CreateMetricsService(ctx, cfg, servicePorts)
167+
if err != nil {
168+
log.Info("Could not create metrics Service", "error", err.Error())
169+
}
170+
171+
// CreateServiceMonitors will automatically create the prometheus-operator ServiceMonitor resources
172+
// necessary to configure Prometheus to scrape metrics from this operator.
173+
services := []*v1.Service{service}
174+
175+
// The ServiceMonitor is created in the same namespace where the operator is deployed
176+
_, err = metrics.CreateServiceMonitors(cfg, operatorNs, services)
177+
if err != nil {
178+
log.Info("Could not create ServiceMonitor object", "error", err.Error())
179+
// If this operator is deployed to a cluster without the prometheus-operator running, it will return
180+
// ErrServiceMonitorNotPresent, which can be used to safely skip ServiceMonitor creation.
181+
if err == metrics.ErrServiceMonitorNotPresent {
182+
log.Info("Install prometheus-operator in your cluster to create ServiceMonitor objects", "error", err.Error())
183+
}
184+
}
185+
}
186+
187+
// serveCRMetrics gets the Operator/CustomResource GVKs and generates metrics based on those types.
188+
// It serves those metrics on "http://metricsHost:operatorMetricsPort".
189+
func serveCRMetrics(cfg *rest.Config, operatorNs string) error {
190+
// The function below returns a list of filtered operator/CR specific GVKs. For more control, override the GVK list below
191+
// with your own custom logic. Note that if you are adding third party API schemas, probably you will need to
192+
// customize this implementation to avoid permissions issues.
193+
filteredGVK, err := k8sutil.GetGVKsFromAddToScheme(apis.AddToScheme)
194+
if err != nil {
195+
return err
196+
}
197+
198+
// The metrics will be generated from the namespaces which are returned here.
199+
// NOTE that passing nil or an empty list of namespaces in GenerateAndServeCRMetrics will result in an error.
200+
ns, err := kubemetrics.GetNamespacesForMetrics(operatorNs)
201+
if err != nil {
202+
return err
203+
}
204+
205+
// Generate and serve custom resource specific metrics.
206+
err = kubemetrics.GenerateAndServeCRMetrics(cfg, ns, filteredGVK, metricsHost, operatorMetricsPort)
207+
if err != nil {
208+
return err
209+
}
210+
return nil
211+
}

Diff for: deploy/operator.yaml

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: dv-operator
5+
spec:
6+
replicas: 1
7+
selector:
8+
matchLabels:
9+
name: dv-operator
10+
template:
11+
metadata:
12+
labels:
13+
name: dv-operator
14+
spec:
15+
serviceAccountName: dv-operator
16+
containers:
17+
- name: dv-operator
18+
# Replace this with the built image name
19+
image: REPLACE_IMAGE
20+
command:
21+
- dv-operator
22+
imagePullPolicy: Always
23+
env:
24+
- name: WATCH_NAMESPACE
25+
valueFrom:
26+
fieldRef:
27+
fieldPath: metadata.namespace
28+
- name: POD_NAME
29+
valueFrom:
30+
fieldRef:
31+
fieldPath: metadata.name
32+
- name: OPERATOR_NAME
33+
value: "dv-operator"

Diff for: deploy/role.yaml

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
kind: Role
2+
apiVersion: rbac.authorization.k8s.io/v1
3+
metadata:
4+
name: dv-operator
5+
rules:
6+
- apiGroups:
7+
- ""
8+
resources:
9+
- pods
10+
- services
11+
- services/finalizers
12+
- endpoints
13+
- persistentvolumeclaims
14+
- events
15+
- configmaps
16+
- secrets
17+
verbs:
18+
- create
19+
- delete
20+
- get
21+
- list
22+
- patch
23+
- update
24+
- watch
25+
- apiGroups:
26+
- apps
27+
resources:
28+
- deployments
29+
- daemonsets
30+
- replicasets
31+
- statefulsets
32+
verbs:
33+
- create
34+
- delete
35+
- get
36+
- list
37+
- patch
38+
- update
39+
- watch
40+
- apiGroups:
41+
- monitoring.coreos.com
42+
resources:
43+
- servicemonitors
44+
verbs:
45+
- "get"
46+
- "create"
47+
- apiGroups:
48+
- apps
49+
resources:
50+
- deployments/finalizers
51+
resourceNames:
52+
- dv-operator
53+
verbs:
54+
- "update"
55+
- apiGroups:
56+
- ""
57+
resources:
58+
- pods
59+
verbs:
60+
- get
61+
- apiGroups:
62+
- apps
63+
resources:
64+
- replicasets
65+
- deployments
66+
verbs:
67+
- get

Diff for: deploy/role_binding.yaml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
kind: RoleBinding
2+
apiVersion: rbac.authorization.k8s.io/v1
3+
metadata:
4+
name: dv-operator
5+
subjects:
6+
- kind: ServiceAccount
7+
name: dv-operator
8+
roleRef:
9+
kind: Role
10+
name: dv-operator
11+
apiGroup: rbac.authorization.k8s.io

Diff for: deploy/service_account.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
apiVersion: v1
2+
kind: ServiceAccount
3+
metadata:
4+
name: dv-operator

Diff for: go.mod

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module github.com/jmelis/dv-operator
2+
3+
go 1.13
4+
5+
require (
6+
github.com/operator-framework/operator-sdk v0.17.0
7+
github.com/prometheus/client_golang v1.6.0
8+
github.com/sirupsen/logrus v1.6.0
9+
github.com/spf13/pflag v1.0.5
10+
k8s.io/api v0.17.4
11+
k8s.io/apimachinery v0.17.4
12+
k8s.io/client-go v12.0.0+incompatible
13+
sigs.k8s.io/controller-runtime v0.5.2
14+
)
15+
16+
replace (
17+
github.com/Azure/go-autorest => github.com/Azure/go-autorest v13.3.2+incompatible // Required by OLM
18+
k8s.io/client-go => k8s.io/client-go v0.17.4 // Required by prometheus-operator
19+
)

0 commit comments

Comments
 (0)