Skip to content

Commit

Permalink
Initial v1alpha2 Workflow controller
Browse files Browse the repository at this point in the history
Signed-off-by: Chris Doherty <[email protected]>
  • Loading branch information
chrisdoherty4 committed Jan 31, 2024
1 parent 49e3ac9 commit d927835
Show file tree
Hide file tree
Showing 40 changed files with 1,041 additions and 173 deletions.
40 changes: 22 additions & 18 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ GOPROXY ?= $(shell go env GOPROXY)

# Runnable tools
GO ?= go
BUF := $(GO) run github.com/bufbuild/buf/cmd/buf@v1.11
CONTROLLER_GEN := $(GO) run sigs.k8s.io/controller-tools/cmd/controller-gen@v0.11
BUF := $(GO) run github.com/bufbuild/buf/cmd/buf@v1.29
CONTROLLER_GEN := $(GO) run sigs.k8s.io/controller-tools/cmd/controller-gen@v0.14
GOFUMPT := $(GO) run mvdan.cc/[email protected]
KUSTOMIZE := $(GO) run sigs.k8s.io/kustomize/kustomize/[email protected]
SETUP_ENVTEST := $(GO) run sigs.k8s.io/controller-runtime/tools/[email protected]
Expand All @@ -39,7 +39,7 @@ help: ## Print this help
@echo
@echo Individual binaries can be built with their name. For example, \`make tink-server\`.
@echo
@echo Individual images can be built with their name appended with -image. For example,
@echo Individual images can be built with their name appended with -image. For example,
@echo \`make tink-server-image\`.

# Version defines the string injected into binaries that indicates the version of the build.
Expand All @@ -49,21 +49,21 @@ help: ## Print this help
VERSION ?= $(shell git rev-parse --short HEAD)

# Define all the binaries we build for this project that get packaged into containers.
BINARIES := tink-server tink-agent tink-worker tink-controller virtual-worker
BINARIES := tink-server tink-agent tink-worker tink-controller tink-controller-v1alpha2 virtual-worker

.PHONY: build
build: $(BINARIES) ## Build all tink binaries. Cross build by setting GOOS and GOARCH.

# Create targets for all the binaries we build. They can be individually invoked with `make <binary>`.
# For example, `make tink-server`. Callers can cross build by defining the GOOS and GOARCH
# For example, `make tink-server`. Callers can cross build by defining the GOOS and GOARCH
# variables. For example, `GOOS=linux GOARCH=arm64 make tink-server`.
# See https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html.
.PHONY: $(BINARIES)
$(BINARIES):
CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GO) build $(LDFLAGS) -o ./bin/$@-$(GOOS)-$(GOARCH) ./cmd/$@

# IMAGE_ARGS is resolved when its used in the `%-image` targets. Consequently, the $* automatic
# variable isn't evaluated until the target is called.
# IMAGE_ARGS is resolved when its used in the `%-image` targets. Consequently, the $* automatic
# variable isn't evaluated until the target is called.
# See https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html.
IMAGE_ARGS ?= -t $*

Expand All @@ -77,8 +77,8 @@ images: $(addsuffix -image,$(BINARIES)) ## Build all tink container images. All
# We only build Linux images so we need to force binaries to be built for Linux. Exporting the
# GOOS variable ensures the recipe's binary dependency is built for Linux.
#
# The $$* leverages .SECONDEXPANSION to specify the matched part of the target name as a
# dependency. In doing so, we ensure the binary is built so it can be copied into the image. For
# The $$* leverages .SECONDEXPANSION to specify the matched part of the target name as a
# dependency. In doing so, we ensure the binary is built so it can be copied into the image. For
# example, `make tink-server-image` will depend on `tink-server`.
# See https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html.
# See https://www.gnu.org/software/make/manual/html_node/Secondary-Expansion.html.
Expand Down Expand Up @@ -109,37 +109,41 @@ generate-proto: buf.gen.yaml buf.lock $(shell git ls-files '**/*.proto') _protoc
$(GOFUMPT) -w internal/proto/workflow/v2/*.pb.*

.PHONY: generate
generate: generate-proto generate-go generate-manifests ## Generate code, manifests etc.
generate: ## Generate code, manifests etc.
generate: generate-proto generate-go generate-manifests

.PHONY: generate-go
generate-go:
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate/boilerplate.generatego.txt" paths="./api/..."
$(GOFUMPT) -w ./api

.PHONY: generate-manifests
generate-manifests: generate-crds generate-rbacs generate-server-rbacs ## Generate manifests e.g. CRD, RBAC etc.
generate-manifests: ## Generate manifests e.g. CRD, RBAC etc.
generate-manifests: generate-crds generate-rbac

.PHONY: generate-crds
generate-crds:
$(CONTROLLER_GEN) \
paths=./api/... \
crd:crdVersions=v1 \
rbac:roleName=manager-role \
output:crd:dir=./config/crd/bases \
output:webhook:dir=./config/webhook \
webhook
$(YAMLFMT) ./config/crd/bases/* ./config/webhook/*

.PHONY: generate-rbacs
generate-rbacs:
.PHONY:
generate-rbac: generate-manager-rbac generate-server-rbac

.PHONY: generate-controller-rbac
generate-manager-rbac:
$(CONTROLLER_GEN) \
paths=./internal/controller/... \
output:rbac:dir=./config/rbac/ \
paths=./internal/workflow/... \
output:rbac:dir=./config/manager-rbac/ \
rbac:roleName=manager-role
$(YAMLFMT) ./config/rbac/*

.PHONY: generate-server-rbacs
generate-server-rbacs:
.PHONY: generate-server-rbac
generate-server-rbac:
$(CONTROLLER_GEN) \
paths=./internal/server/... \
output:rbac:dir=./config/server-rbac \
Expand Down
1 change: 0 additions & 1 deletion api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion api/v1alpha2/conditions.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type Condition struct {
Status ConditionStatus `json:"status"`

// LastTransition is the last time the condition transitioned from one status to another.
LastTransition *metav1.Time `json:"lastTransitionTime"`
LastTransition metav1.Time `json:"lastTransitionTime"`

// Reason is a short CamelCase description for the conditions last transition.
// +optional
Expand Down
4 changes: 3 additions & 1 deletion api/v1alpha2/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,17 @@ type WorkflowStatus struct {

// StartedAt is the time the first action was requested. Nil indicates the Workflow has not
// started.
// +optional
StartedAt *metav1.Time `json:"startedAt,omitempty"`

// LastTransition is the observed time when State transitioned last.
LastTransition *metav1.Time `json:"lastTransitioned,omitempty"`
LastTransition metav1.Time `json:"lastTransitioned,omitempty"`

// State describes the current state of the Workflow.
State WorkflowState `json:"state,omitempty"`

// Conditions details a set of observations about the Workflow.
// +optional
Conditions Conditions `json:"conditions"`
}

Expand Down
11 changes: 2 additions & 9 deletions api/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions buf.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ deps:
owner: googleapis
repository: googleapis
commit: a86849a25cc04f4dbe9b15ddddfbc488
digest: shake256:e19143328f8cbfe13fc226aeee5e63773ca494693a72740a7560664270039a380d94a1344234b88c7691311460df9a9b1c2982190d0a2612eae80368718e1943
10 changes: 10 additions & 0 deletions cmd/tink-controller-v1alpha2/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM alpine:3.15

ARG TARGETOS
ARG TARGETARCH

RUN apk add --no-cache --update --upgrade ca-certificates

COPY bin/tink-controller-v1alpha2-${TARGETOS}-${TARGETARCH} /usr/bin/tink-controller

ENTRYPOINT ["/usr/bin/tink-controller"]
179 changes: 179 additions & 0 deletions cmd/tink-controller-v1alpha2/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package main

import (
"fmt"
"os"
"strings"

"github.com/go-logr/logr"
"github.com/go-logr/zapr"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
tinkv1 "github.com/tinkerbell/tink/api/v1alpha2"
"github.com/tinkerbell/tink/internal/workflow"
"go.uber.org/zap"
"k8s.io/apimachinery/pkg/runtime"
amruntimeutil "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/metrics/server"
)

// version is set at build time.
var version = "devel"

// scheme is passed to the manager.
var scheme = runtime.NewScheme()

func init() {
amruntimeutil.Must(tinkv1.AddToScheme(scheme))

//+kubebuilder:scaffold:scheme
}

type Config struct {
K8sAPI string
Kubeconfig string // only applies to out of cluster
MetricsAddr string
ProbeAddr string
EnableLeaderElection bool
}

func (c *Config) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&c.K8sAPI, "kubernetes", "",
"The Kubernetes API URL, used for in-cluster client construction.")
fs.StringVar(&c.Kubeconfig, "kubeconfig", "", "Absolute path to the kubeconfig file")
fs.StringVar(&c.MetricsAddr, "metrics-bind-address", ":8080",
"The address the metric endpoint binds to.")
fs.StringVar(&c.ProbeAddr, "health-probe-bind-address", ":8081",
"The address the probe endpoint binds to.")
fs.BoolVar(&c.EnableLeaderElection, "leader-elect", false,
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
}

func main() {
cmd := NewRootCommand()
if err := cmd.Execute(); err != nil {
os.Exit(1)
}
}

func NewRootCommand() *cobra.Command {
var config Config

zlog, err := zap.NewProduction()
if err != nil {
panic(err)
}
logger := zapr.NewLogger(zlog).WithName("github.com/tinkerbell/tink")

cmd := &cobra.Command{
Use: "tink-controller",
PreRunE: func(cmd *cobra.Command, args []string) error {
viper, err := createViper(logger)
if err != nil {
return fmt.Errorf("config init: %w", err)
}
return applyViper(viper, cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
logger.Info("Starting controller version " + version)

ccfg := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
&clientcmd.ClientConfigLoadingRules{ExplicitPath: config.Kubeconfig},
&clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: config.K8sAPI}})

cfg, err := ccfg.ClientConfig()
if err != nil {
return err
}

namespace, _, err := ccfg.Namespace()
if err != nil {
return err
}

opts := ctrl.Options{
Logger: logger,
LeaderElection: config.EnableLeaderElection,
LeaderElectionID: "tink.tinkerbell.org",
LeaderElectionNamespace: namespace,
Metrics: server.Options{
BindAddress: config.MetricsAddr,
},
HealthProbeBindAddress: config.ProbeAddr,
Scheme: scheme,
}

mgr, err := ctrl.NewManager(cfg, opts)
if err != nil {
return err
}

if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
return err
}

if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
return err
}

if err := workflow.NewReconciler(mgr.GetClient()).SetupWithManager(mgr); err != nil {
return err
}

return mgr.Start(cmd.Context())
},
}
config.AddFlags(cmd.Flags())
return cmd
}

func createViper(logger logr.Logger) (*viper.Viper, error) {
v := viper.New()
v.AutomaticEnv()
v.SetConfigName("tink-controller")
v.AddConfigPath("/etc/tinkerbell")
v.AddConfigPath(".")
v.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))

// If a config file is found, read it in.
if err := v.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return nil, fmt.Errorf("loading config file: %w", err)
}
logger.Info("no config file found")
} else {
logger.Info("loaded config file", "configFile", v.ConfigFileUsed())
}

return v, nil
}

func applyViper(v *viper.Viper, cmd *cobra.Command) error {
errors := []error{}

cmd.Flags().VisitAll(func(f *pflag.Flag) {
if !f.Changed && v.IsSet(f.Name) {
val := v.Get(f.Name)
if err := cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val)); err != nil {
errors = append(errors, err)
return
}
}
})

if len(errors) > 0 {
errs := []string{}
for _, err := range errors {
errs = append(errs, err.Error())
}
return fmt.Errorf(strings.Join(errs, ", "))
}

return nil
}
2 changes: 1 addition & 1 deletion cmd/tink-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"github.com/tinkerbell/tink/internal/controller"
"github.com/tinkerbell/tink/internal/deprecated/controller"
"go.uber.org/zap"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
Expand Down
Loading

0 comments on commit d927835

Please sign in to comment.