Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1alpha2 Workflow Controller #874

Merged
merged 2 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .yamllint
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
---
extends: default

rules:
Expand All @@ -14,6 +13,8 @@ rules:
max: 160
allow-non-breakable-inline-mappings: true
truthy: disable
indentation:
indent-sequences: whatever

ignore: |
out
24 changes: 14 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not too excited we have to use tink-controller-v1alpha2 :(


.PHONY: build
build: $(BINARIES) ## Build all tink binaries. Cross build by setting GOOS and GOARCH.
Expand Down Expand Up @@ -97,37 +97,41 @@ generate-proto: buf.gen.yaml buf.lock $(shell git ls-files '**/*.proto') $(BUF)
$(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) $(GOFUMPT)
$(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) $(YAMLFMT)
$(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: $(CONTROLLER_GEN) $(YAMLFMT)
.PHONY: generate-rbac
generate-rbac: $(CONTROLLER_GEN) $(YAMLFMT)

.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: $(CONTROLLER_GEN) $(YAMLFMT)
.PHONY: generate-server-rbac
generate-server-rbac: $(CONTROLLER_GEN) $(YAMLFMT)
$(CONTROLLER_GEN) \
paths=./internal/server/... \
output:rbac:dir=./config/server-rbac \
Expand Down
2 changes: 1 addition & 1 deletion Tools.mk
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ PROTOC_GEN_GO_GRPC := $(TOOLS_DIR)/protoc-gen-go-grpc
PROTOC_GEN_GO_VER := v1.28
PROTOC_GEN_GO := $(TOOLS_DIR)/protoc-gen-go

CONTROLLER_GEN_VER := v0.11
CONTROLLER_GEN_VER := v0.14
CONTROLLER_GEN := $(TOOLS_DIR)/controller-gen-$(CONTROLLER_GEN_VER)

KUSTOMIZE_VER := v4.5
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"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you love pointers ;) so why move away from one here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't optional because we expect the LastTransition to be updated even if we're populating the first status.


// 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.

2 changes: 1 addition & 1 deletion buf.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ deps:
- remote: buf.build
owner: googleapis
repository: googleapis
commit: e874a0be2bf140a5a4c7d4122c635823
commit: 7e6f6e774e29406da95bd61cdcdbc8bc
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason not to use scratch?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know. The goal of the PR is to get something functional so I've not experimented.


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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is v1alpha2 so different from a cmd/cli perspective that we have to break it out entirely? Could a flag be used in the existing tink-controller?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open to using a single binary. This temporary extra binary just felt simpler to me. There won't be a transition period where both APIs are supported so eventually tink-controller-v1alpha2 will become tink-controller.


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")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need a default location for a config file? does this provide much value, you think?

Copy link
Member Author

@chrisdoherty4 chrisdoherty4 Feb 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not; this is copy-n-paste from the existing tink-controller. If we maintain the temporary v1alpha2 binary, I'll reduce this down to bare minimum so we don't end up with cruft down the road.

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
Loading