Skip to content

Commit 3aae084

Browse files
author
David Festal
committed
First sketch of the GO API
1 parent c834573 commit 3aae084

27 files changed

+1942
-3
lines changed

.gitignore

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,77 @@
1+
# Temporary Build Files
2+
build/_output
3+
build/_test
4+
# Created by https://www.gitignore.io/api/go,vim,emacs,visualstudiocode
5+
### Emacs ###
6+
# -*- mode: gitignore; -*-
7+
*~
8+
\#*\#
9+
/.emacs.desktop
10+
/.emacs.desktop.lock
11+
*.elc
12+
auto-save-list
13+
tramp
14+
.\#*
15+
# Org-mode
16+
.org-id-locations
17+
*_archive
18+
# flymake-mode
19+
*_flymake.*
20+
# eshell files
21+
/eshell/history
22+
/eshell/lastdir
23+
# elpa packages
24+
/elpa/
25+
# reftex files
26+
*.rel
27+
# AUCTeX auto folder
28+
/auto/
29+
# cask packages
30+
.cask/
31+
dist/
32+
# Flycheck
33+
flycheck_*.el
34+
# server auth directory
35+
/server/
36+
# projectiles files
37+
.projectile
38+
projectile-bookmarks.eld
39+
# directory configuration
40+
.dir-locals.el
41+
# saveplace
42+
places
43+
# url cache
44+
url/cache/
45+
# cedet
46+
ede-projects.el
47+
# smex
48+
smex-items
49+
# company-statistics
50+
company-statistics-cache.el
51+
# anaconda-mode
52+
anaconda-mode/
53+
### Go ###
154
# Binaries for programs and plugins
255
*.exe
356
*.exe~
457
*.dll
558
*.so
659
*.dylib
7-
8-
# Test binary, build with `go test -c`
60+
# Test binary, build with 'go test -c'
961
*.test
10-
1162
# Output of the go coverage tool, specifically when used with LiteIDE
1263
*.out
64+
### Vim ###
65+
# swap
66+
.sw[a-p]
67+
.*.sw[a-p]
68+
# session
69+
Session.vim
70+
# temporary
71+
.netrwhist
72+
# auto-generated tag files
73+
tags
74+
### VisualStudioCode ###
75+
.vscode/*
76+
.history
77+
# End of https://www.gitignore.io/api/go,vim,emacs,visualstudiocode

build/Dockerfile

Lines changed: 15 additions & 0 deletions
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/devworkspace-api \
4+
USER_UID=1001 \
5+
USER_NAME=devworkspace-api
6+
7+
# install operator binary
8+
COPY build/_output/bin/devworkspace-api ${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}

build/bin/entrypoint

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/bin/sh -e
2+
3+
# This is documented here:
4+
# https://docs.openshift.com/container-platform/3.11/creating_images/guidelines.html#openshift-specific-guidelines
5+
6+
if ! whoami &>/dev/null; then
7+
if [ -w /etc/passwd ]; then
8+
echo "${USER_NAME:-devworkspace-api}:x:$(id -u):$(id -g):${USER_NAME:-devworkspace-api} user:${HOME}:/sbin/nologin" >> /etc/passwd
9+
fi
10+
fi
11+
12+
exec ${OPERATOR} $@

build/bin/user_setup

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
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+
mkdir -p ${HOME}
6+
chown ${USER_UID}:0 ${HOME}
7+
chmod ug+rwx ${HOME}
8+
9+
# runtime user will need to be able to self-insert in /etc/passwd
10+
chmod g+rw /etc/passwd
11+
12+
# no need for this script to remain in the image after running
13+
rm $0

cmd/manager/main.go

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

0 commit comments

Comments
 (0)