Skip to content

Commit

Permalink
Revamp webhooks for OSM (#151)
Browse files Browse the repository at this point in the history
* Add more linters

Signed-off-by: Waleed Malik <[email protected]>

* Upgrade golangci-lint to v1.44.0 in prow jobs

Signed-off-by: Waleed Malik <[email protected]>

* Fix template comment spacing in default OSPs

Signed-off-by: Waleed Malik <[email protected]>

* Refactor webhooks for OSM

Signed-off-by: Waleed Malik <[email protected]>

Unmarshal provider configs in strict mode

* Domain name is not required when using application credentials (#154)

Signed-off-by: Waleed Malik <[email protected]>

* Disable firewalld on supported OSes (#155)

* Disable firewalld on supported OSes

Signed-off-by: Artiom Diomin <[email protected]>

* Further firewalld cleanup in RHEL OSP

Signed-off-by: Artiom Diomin <[email protected]>

Update testdata

Signed-off-by: Waleed Malik <[email protected]>

Additional RBAC for webhooks

Signed-off-by: Waleed Malik <[email protected]>

Refactored code

Signed-off-by: Waleed Malik <[email protected]>

Refactored code

Signed-off-by: Waleed Malik <[email protected]>

* Refactored code

Signed-off-by: Waleed Malik <[email protected]>

Co-authored-by: Artiom Diomin <[email protected]>
  • Loading branch information
ahmedwaleedmalik and kron4eg authored Apr 1, 2022
1 parent 3ef2056 commit 70dcdc6
Show file tree
Hide file tree
Showing 81 changed files with 2,897 additions and 3,206 deletions.
42 changes: 31 additions & 11 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,54 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

run:
deadline: 20m
issues-exit-code: 1
skip-dirs:
- hack
- vendor

skip-files:
- zz_generated.*.go

linters:
enable:
- revive
- govet
- gofmt
- structcheck
- varcheck
- unconvert
- ineffassign
- asciicheck
- bidichk
- bodyclose
- deadcode
- durationcheck
- errcheck
- errname
- errorlint
- exportloopref
- goconst
- gocritic
- gocyclo
- misspell
- gofmt
- gosimple
- govet
- ineffassign
- misspell
- noctx
- nolintlint
- predeclared
- promlinter
- revive
- staticcheck
- structcheck
- tenv
- unconvert
- unused
- errcheck
- varcheck
- wastedassign
- whitespace
disable-all: true

linters-settings:
goimports:
local-prefixes: k8c.io/operating-system-manager
tagliatelle:
case:
rules:
json: goCamel
yaml: goCamel
8 changes: 4 additions & 4 deletions .prow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ presubmits:
preset-goproxy: "true"
spec:
containers:
- image: golang:1.17.5
- image: golang:1.17.8
command:
- make
args:
Expand All @@ -61,7 +61,7 @@ presubmits:
preset-goproxy: "true"
spec:
containers:
- image: golang:1.17.5
- image: golang:1.17.8
command:
- make
args:
Expand Down Expand Up @@ -103,7 +103,7 @@ presubmits:
preset-goproxy: "true"
spec:
containers:
- image: golangci/golangci-lint:v1.42.1
- image: golangci/golangci-lint:v1.44.0
command:
- make
args:
Expand All @@ -124,7 +124,7 @@ presubmits:
preset-goproxy: "true"
spec:
containers:
- image: golang:1.17.5
- image: golang:1.17.8
command:
- make
args:
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

ARG GO_VERSION=1.17.5
ARG GO_VERSION=1.17.8
FROM golang:${GO_VERSION} AS builder
WORKDIR /go/src/k8c.io/operating-system-manager
COPY . .
Expand Down
45 changes: 1 addition & 44 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export GO111MODULE=on
export GOFLAGS?=-mod=readonly -trimpath
export GIT_TAG ?= $(shell git tag --points-at HEAD)

GO_VERSION = 1.17.5
GO_VERSION = 1.17.8

CMD = $(notdir $(wildcard ./cmd/*))
BUILD_DEST ?= _build
Expand Down Expand Up @@ -107,49 +107,6 @@ clean:
download-gocache:
@./hack/ci/download-gocache.sh

hack/ci/testdata/ca-key.pem:
openssl genrsa -out hack/ci/testdata/ca-key.pem 4096

hack/ci/testdata/ca-cert.pem: hack/ci/testdata/ca-key.pem
openssl req -x509 -new -nodes -key hack/ci/testdata/ca-key.pem \
-subj "/C=US/ST=CA/O=Acme/CN=k8s-machine-controller-ca" \
-sha256 -days 10000 -out hack/ci/testdata/ca-cert.pem

hack/ci/testdata/admission-key.pem: hack/ci/testdata/ca-cert.pem
openssl genrsa -out hack/ci/testdata/admission-key.pem 2048
chmod 0600 hack/ci/testdata/admission-key.pem

hack/ci/testdata/admission-cert.pem: hack/ci/testdata/admission-key.pem
openssl req -new -sha256 \
-key hack/ci/testdata/admission-key.pem \
-config hack/ci/testdata/webhook-certificate.cnf -extensions v3_req \
-out hack/ci/testdata/admission.csr
openssl x509 -req \
-sha256 \
-days 10000 \
-extensions v3_req \
-extfile hack/ci/testdata/webhook-certificate.cnf \
-in hack/ci/testdata/admission.csr \
-CA hack/ci/testdata/ca-cert.pem \
-CAkey hack/ci/testdata/ca-key.pem \
-CAcreateserial \
-out hack/ci/testdata/admission-cert.pem

clean-certs:
cd hack/ci/testdata/ && rm -f admission.csr admission-cert.pem admission-key.pem ca-cert.pem ca-key.pem

.PHONY: deploy
deploy: hack/ci/testdata/admission-cert.pem
@cat deploy/operating-system-manager.yaml \
|sed "s/__worker_cluster_kubeconfig__/$(shell cat ~/.kube/config|$(BASE64_ENC))/g" \
|kubectl apply -f -

@cat deploy/operating-system-manager-webhook.yaml \
|sed "s/__admission_cert__/$(shell cat hack/ci/testdata/admission-cert.pem|$(BASE64_ENC))/g" \
|sed "s/__admission_key__/$(shell cat hack/ci/testdata/admission-key.pem|$(BASE64_ENC))/g" \
|sed "s/__admission_ca_cert__/$(shell cat hack/ci/testdata/ca-cert.pem|$(BASE64_ENC))/g" \
|kubectl apply -f -

.PHONY: docker-image
docker-image:
docker build --build-arg GO_VERSION=$(GO_VERSION) -t $(IMAGE_NAME) .
Expand Down
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This project is experimental and currently a work-in-progress. **This is not sup

### Problem Statement

[Machine-Controller](https://github.com/kubermatic/machine-controller) can be used to create and manage worker nodes in a kubernetes clusters. For each supported operating system(based on the cloud provider), a specific plugin is used to generate cloud configs. These configs are then injected in the worker nodes using either [cloud-init](https://cloud-init.io/) or (ignition)[https://coreos.github.io/ignition/] based on the operating system. Finally the nodes are bootstrapped.
[Machine-Controller](https://github.com/kubermatic/machine-controller) can be used to create and manage worker nodes in a kubernetes clusters. For each supported operating system(based on the cloud provider), a specific plugin is used to generate cloud configs. These configs are then injected in the worker nodes using either [cloud-init](https://cloud-init.io/) or [ignition](https://coreos.github.io/ignition/) based on the operating system. Finally the nodes are bootstrapped.

Currently this workflow has the following limitations/issues:

Expand All @@ -29,7 +29,6 @@ Operating System Manager was created to solve the above mentioned issues. It dec

OSM introduces the following resources:


### OperatingSystemProfile

Templatized resource that represents the details of each operating system. OSPs are immutable and default OSPs for supported operating systems are provided/installed automatically by kubermatic. End users can create custom OSPs as well to fit their own use-cases.
Expand All @@ -42,7 +41,6 @@ Immutable resource that contains the actual configurations that are going to be

Its dedicated controller runs in the **seed** cluster, in user cluster namespace, and is responsible for generating the OSCs in **seed** and secrets in `cloud-init-settings` namespace in the user cluster.


For each cluster there are at least two OSC objects:

1. **Bootstrap**: OSC used for initial configuration of machine and to fetch the provisioning OSC object.
Expand All @@ -64,9 +62,15 @@ Information about supported OS versions can be found [here](./docs/compatibility

## Deploy OSM

[TBD]
- Install [cert-manager](https://cert-manager.io/) for generating certificates used by webhooks since they serve using HTTPS

```terminal
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.7.1/cert-manager.yaml
```

_The code and sample YAML files in the master branch of the operating-system-manager repository are under active development and are not guaranteed to be stable. Use them at your own risk!_
- Run `kubectl create namespace cloud-init-settings` to create namespace where secrets against OSC are stored
- Run `kubectl apply -f deploy/crd/` to install CRDs
- Run `kubectl apply -f deploy/` to deploy OSM

## Development

Expand Down
13 changes: 3 additions & 10 deletions cmd/osm-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,10 @@ import (
type options struct {
workerCount int
namespace string
clusterName string
containerRuntime string
externalCloudProvider bool
pauseImage string
initialTaints string
nodePortRange string
podCidr string
enableLeaderElection bool
clusterDNSIPs string
workerClusterKubeconfig string
Expand Down Expand Up @@ -102,8 +99,6 @@ func main() {
flag.StringVar(&opt.pauseImage, "pause-image", "", "pause image to use in Kubelet.")
flag.StringVar(&opt.initialTaints, "initial-taints", "", "taints to use when creating the node.")

flag.StringVar(&opt.podCidr, "pod-cidr", "172.25.0.0/16", "The network ranges from which POD networks are allocated")
flag.StringVar(&opt.nodePortRange, "node-port-range", "30000-32767", "A port range to reserve for services with NodePort visibility")
flag.StringVar(&opt.kubeletFeatureGates, "node-kubelet-feature-gates", "RotateKubeletServerCertificate=true", "Feature gates to set on the kubelet")

flag.StringVar(&opt.nodeHTTPProxy, "node-http-proxy", "", "If set, it configures the 'HTTP_PROXY' & 'HTTPS_PROXY' environment variable on the nodes.")
Expand Down Expand Up @@ -240,8 +235,6 @@ func main() {
opt.initialTaints,
opt.nodeHTTPProxy,
opt.nodeNoProxy,
opt.nodePortRange,
opt.podCidr,
containerRuntimeConfig,
opt.nodeRegistryCredentialsSecret,
parsedKubeletFeatureGates,
Expand Down Expand Up @@ -272,15 +265,15 @@ func createManager(opt *options) (manager.Manager, error) {

mgr, err := manager.New(config.GetConfigOrDie(), options)
if err != nil {
return nil, fmt.Errorf("error building ctrlruntime manager: %v", err)
return nil, fmt.Errorf("error building ctrlruntime manager: %w", err)
}

// Add health endpoints
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
return nil, fmt.Errorf("failed to add health check: %v", err)
return nil, fmt.Errorf("failed to add health check: %w", err)
}
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
return nil, fmt.Errorf("failed to add readiness check: %v", err)
return nil, fmt.Errorf("failed to add readiness check: %w", err)
}
return mgr, nil
}
Expand Down
79 changes: 47 additions & 32 deletions cmd/webhook/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,41 @@ package main

import (
"flag"

"go.uber.org/zap"

clusterv1alpha1 "github.com/kubermatic/machine-controller/pkg/apis/cluster/v1alpha1"
"k8c.io/operating-system-manager/pkg/admission"
mdvalidation "k8c.io/operating-system-manager/pkg/admission/machinedeployment/validation"
oscvalidation "k8c.io/operating-system-manager/pkg/admission/operatingsystemconfig/validation"
ospvalidation "k8c.io/operating-system-manager/pkg/admission/operatingsystemprofile/validation"
"k8c.io/operating-system-manager/pkg/crd/osm/v1alpha1"

"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/klog"
ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client/config"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/manager"
)

type options struct {
namespace string

admissionListenAddress string
admissionTLSCertPath string
admissionTLSKeyPath string
metricsAddr string
enableLeaderElection bool
probeAddr string
certDir string
}

var (
scheme = runtime.NewScheme()
)

func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))

utilruntime.Must(v1alpha1.AddToScheme(scheme))
utilruntime.Must(clusterv1alpha1.AddToScheme(scheme))
}
Expand All @@ -51,45 +62,49 @@ func main() {

opt := &options{}

flag.StringVar(&opt.admissionListenAddress, "listen-address", ":9876", "The address on which the MutatingWebhook will listen on")
flag.StringVar(&opt.admissionTLSCertPath, "tls-cert-path", "/tmp/cert/cert.pem", "The path of the TLS cert for the MutatingWebhook")
flag.StringVar(&opt.admissionTLSKeyPath, "tls-key-path", "/tmp/cert/key.pem", "The path of the TLS key for the MutatingWebhook")
flag.StringVar(&opt.metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&opt.probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.BoolVar(&opt.enableLeaderElection, "leader-elect", false,
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
flag.StringVar(&opt.namespace, "namespace", "", "The namespace where the OSC webhook will run.")
flag.StringVar(&opt.certDir, "cert-dir", "/tmp/k8s-webhook-server/serving-certs",
"Directory that contains the server key(tls.key) and certificate(tls.crt).")
flag.Parse()

if len(opt.namespace) == 0 {
klog.Fatal("-namespace is required")
}

// Build config for in-cluster cluster
cfg, err := config.GetConfig()
if err != nil {
klog.Fatalf("error building kubeconfig: %v", err)
}

// Build client against in-cluster config
client, err := ctrlruntimeclient.New(cfg, ctrlruntimeclient.Options{
Scheme: scheme,
mgr, err := manager.New(config.GetConfigOrDie(), manager.Options{
CertDir: opt.certDir,
HealthProbeBindAddress: opt.probeAddr,
LeaderElection: opt.enableLeaderElection,
LeaderElectionNamespace: opt.namespace,
LeaderElectionID: "operating-system-manager-leader-lock",
MetricsBindAddress: opt.metricsAddr,
Port: 9443,
Scheme: scheme,
})
if err != nil {
klog.Fatalf("failed to build seed client: %v", err)
klog.Fatal("failed to create the manager", zap.Error(err))
}

srv, err := admission.New(opt.admissionListenAddress, opt.namespace, client)
if err != nil {
klog.Fatalf("failed to create admission hook: %v", err)
}
// Register webhooks
oscvalidation.NewAdmissionHandler().SetupWebhookWithManager(mgr)
ospvalidation.NewAdmissionHandler().SetupWebhookWithManager(mgr)
mdvalidation.NewAdmissionHandler(mgr.GetClient(), opt.namespace).SetupWebhookWithManager(mgr)

klog.Infof("starting webhook server on %s", opt.admissionListenAddress)
// Add health endpoints
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
klog.Fatalf("failed to add health check: %v", zap.Error(err))
}
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
klog.Fatalf("failed to add readiness check: %v", zap.Error(err))
}

if err := srv.ListenAndServeTLS(opt.admissionTLSCertPath, opt.admissionTLSKeyPath); err != nil {
klog.Fatalf("failed to start server: %v", err)
klog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
klog.Fatalf("failed to start OSC controller: %v", zap.Error(err))
}
defer func() {
if err := srv.Close(); err != nil {
klog.Fatalf("failed to shutdown server: %v", err)
}
}()
klog.Infof("Listening on %s", opt.admissionListenAddress)
select {}
}
Loading

0 comments on commit 70dcdc6

Please sign in to comment.