commit 1354e83813986175c78cd4d3c4dd368ad949cf4f1c724e3c68fe17de27b86019
| author | 斟酌 鵬兄 <tgckpg@gmail.com> |
| date | 2026-04-24T16:46:43Z |
| subject | Added: ctl create controller |
commit 1354e83813986175c78cd4d3c4dd368ad949cf4f1c724e3c68fe17de27b86019
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date: 2026-04-24T16:46:43Z
Added: ctl create controller
---
clitools/devtools/run-controller.sh | 17 -
clitools/devtools/run.sh | 20 +
.../pkg/apis/monok8s/v1alpha1/groupversion_info.go | 1 +
clitools/pkg/apis/monok8s/v1alpha1/osupgrade.go | 12 +-
clitools/pkg/cmd/agent/agent.go | 11 +-
clitools/pkg/cmd/controller/controller.go | 8 +-
clitools/pkg/cmd/create/create.go | 27 +-
clitools/pkg/cmd/root/root.go | 2 +-
clitools/pkg/controller/server.go | 4 +
clitools/pkg/node/agent.go | 31 +-
clitools/pkg/node/controller.go | 405 ---------------------
clitools/pkg/render/controller.go | 278 ++++++++++++++
clitools/pkg/render/monoks.go | 6 +-
13 files changed, 372 insertions(+), 450 deletions(-)
diff --git a/clitools/devtools/run-controller.sh b/clitools/devtools/run-controller.sh
deleted file mode 100755
index 395b2f1..0000000
--- a/clitools/devtools/run-controller.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-
-SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
-PROJ_ROOT="$( realpath "$SCRIPT_DIR"/../ )"
-OUT_DIR="$PROJ_ROOT"/out
-
-if [ -f "$OUT_DIR/tls.key" ] && [ -f "$OUT_DIR/tls.crt" ]; then
- echo "Use existing certs"
-else
- echo "Generating self signed certs"
- openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
- -keyout "$OUT_DIR"/tls.key -out "$OUT_DIR"/tls.crt \
- -subj "/CN=127.0.0.1" \
- -addext "subjectAltName=IP:127.0.0.1,DNS:localhost"
-fi
-
-go run "$PROJ_ROOT"/cmd/ctl controller --tls-cert-file "$OUT_DIR"/tls.crt --tls-private-key-file "$OUT_DIR"/tls.key
diff --git a/clitools/devtools/run.sh b/clitools/devtools/run.sh
new file mode 100755
index 0000000..ba5f74b
--- /dev/null
+++ b/clitools/devtools/run.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
+PROJ_ROOT="$( realpath "$SCRIPT_DIR"/../ )"
+OUT_DIR="$PROJ_ROOT"/out
+
+if [ "$1" == "controller" ]; then
+ if [ -f "$OUT_DIR/tls.key" ] && [ -f "$OUT_DIR/tls.crt" ]; then
+ echo "Use existing certs"
+ else
+ echo "Generating self signed certs"
+ openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
+ -keyout "$OUT_DIR"/tls.key -out "$OUT_DIR"/tls.crt \
+ -subj "/CN=127.0.0.1" \
+ -addext "subjectAltName=IP:127.0.0.1,DNS:localhost"
+ fi
+ go run "$PROJ_ROOT"/cmd/ctl $@ --tls-cert-file "$OUT_DIR"/tls.crt --tls-private-key-file "$OUT_DIR"/tls.key
+else
+ go run "$PROJ_ROOT"/cmd/ctl $@
+fi
diff --git a/clitools/pkg/apis/monok8s/v1alpha1/groupversion_info.go b/clitools/pkg/apis/monok8s/v1alpha1/groupversion_info.go
index 31a05cc..66f091f 100644
--- a/clitools/pkg/apis/monok8s/v1alpha1/groupversion_info.go
+++ b/clitools/pkg/apis/monok8s/v1alpha1/groupversion_info.go
@@ -16,6 +16,7 @@ var (
AltPartDeviceLink = "/dev/mksaltpart"
BootStateFile = "/run/monok8s/boot-state.env"
CatalogURL = "https://example.com/monok8s.io/v1alpha1/catalog.yaml"
+ ControlAgentName = "control-agent"
ControlAgentKey = "monok8s.io/control-agent"
EnvConfigDir = "/opt/monok8s/config"
Label = "monok8s.io/label"
diff --git a/clitools/pkg/apis/monok8s/v1alpha1/osupgrade.go b/clitools/pkg/apis/monok8s/v1alpha1/osupgrade.go
index 0f58277..dd66bf7 100644
--- a/clitools/pkg/apis/monok8s/v1alpha1/osupgrade.go
+++ b/clitools/pkg/apis/monok8s/v1alpha1/osupgrade.go
@@ -53,6 +53,10 @@ type OSUpgradeSpec struct {
// +kubebuilder:validation:Enum=fast;balanced;safe
// +kubebuilder:default=balanced
+ // Profiles (TODO)
+ // safe - api-server can be responsive most of the time
+ // balanced - api-server can sometimes be unresponsive
+ // fast - disable throttling. Good for worker node.
FlashProfile string `json:"flashProfile,omitempty" yaml:"flashProfile,omitempty"`
Catalog *VersionCatalogSource `json:"catalog,omitempty" yaml:"catalog,omitempty"`
@@ -100,7 +104,13 @@ type OSUpgradeProgressList struct {
type OSUpgradeProgressSpec struct {
SourceRef OSUpgradeSourceRef `json:"sourceRef,omitempty" yaml:"sourceRef,omitempty"`
- NodeName string `json:"nodeName,omitempty" yaml:"nodeName,omitempty"`
+
+ // RetryNonce triggers a retry when its value changes.
+ // Users can update this field (for example, set it to the current time)
+ // to request a retry of a failed OS upgrade.
+ RetryNonce string `json:"retryNonce,omitempty" yaml:"retryNonce,omitempty"`
+
+ NodeName string `json:"nodeName,omitempty" yaml:"nodeName,omitempty"`
}
type OSUpgradeSourceRef struct {
diff --git a/clitools/pkg/cmd/agent/agent.go b/clitools/pkg/cmd/agent/agent.go
index e3c5420..3273f93 100644
--- a/clitools/pkg/cmd/agent/agent.go
+++ b/clitools/pkg/cmd/agent/agent.go
@@ -21,13 +21,17 @@ import (
)
func NewCmdAgent(flags *genericclioptions.ConfigFlags) *cobra.Command {
- var namespace string
var envFile string
cmd := &cobra.Command{
Use: "agent --env-file path",
Short: "Watch OSUpgradeProgress resources for this node and process upgrades",
RunE: func(cmd *cobra.Command, _ []string) error {
+ ns, _, err := flags.ToRawKubeConfigLoader().Namespace()
+ if err != nil {
+ return err
+ }
+
if envFile == "" {
return fmt.Errorf("--env-file is required")
}
@@ -51,7 +55,7 @@ func NewCmdAgent(flags *genericclioptions.ConfigFlags) *cobra.Command {
klog.InfoS("starting agent",
"node", cfg.Spec.NodeName,
- "namespace", namespace,
+ "namespace", ns,
"envFile", envFile,
)
@@ -60,11 +64,10 @@ func NewCmdAgent(flags *genericclioptions.ConfigFlags) *cobra.Command {
return fmt.Errorf("create kube clients: %w", err)
}
- return runWatchLoop(ctx, clients, namespace, cfg.Spec.NodeName)
+ return runWatchLoop(ctx, clients, ns, cfg.Spec.NodeName)
},
}
- cmd.Flags().StringVar(&namespace, "namespace", templates.DefaultNamespace, "namespace to watch")
cmd.Flags().StringVar(&envFile, "env-file", "", "path to env file containing MKS_* variables")
return cmd
diff --git a/clitools/pkg/cmd/controller/controller.go b/clitools/pkg/cmd/controller/controller.go
index fb4bb72..be93a8b 100644
--- a/clitools/pkg/cmd/controller/controller.go
+++ b/clitools/pkg/cmd/controller/controller.go
@@ -15,7 +15,6 @@ import (
mkscontroller "example.com/monok8s/pkg/controller"
osupgradectrl "example.com/monok8s/pkg/controller/osupgrade"
"example.com/monok8s/pkg/kube"
- "example.com/monok8s/pkg/templates"
)
type ServerConfig struct {
@@ -31,6 +30,12 @@ func NewCmdController(flags *genericclioptions.ConfigFlags) *cobra.Command {
Use: "controller",
Short: "Start a controller that handles OSUpgrade resources",
RunE: func(cmd *cobra.Command, _ []string) error {
+ ns, _, err := flags.ToRawKubeConfigLoader().Namespace()
+ if err != nil {
+ return err
+ }
+ conf.Namespace = ns
+
ctx := cmd.Context()
klog.InfoS("starting controller", "namespace", conf.Namespace)
@@ -79,7 +84,6 @@ func NewCmdController(flags *genericclioptions.ConfigFlags) *cobra.Command {
},
}
- cmd.Flags().StringVar(&conf.Namespace, "namespace", templates.DefaultNamespace, "namespace to watch")
cmd.Flags().StringVar(&conf.TLSCertFile, "tls-cert-file", conf.TLSCertFile,
"File containing x509 Certificate used for serving HTTPS (with intermediate certs, if any, concatenated after server cert).")
cmd.Flags().StringVar(&conf.TLSPrivateKeyFile, "tls-private-key-file", conf.TLSPrivateKeyFile,
diff --git a/clitools/pkg/cmd/create/create.go b/clitools/pkg/cmd/create/create.go
index d326ca7..018c0ab 100644
--- a/clitools/pkg/cmd/create/create.go
+++ b/clitools/pkg/cmd/create/create.go
@@ -3,11 +3,12 @@ package create
import (
"fmt"
"github.com/spf13/cobra"
+ "k8s.io/cli-runtime/pkg/genericclioptions"
render "example.com/monok8s/pkg/render"
)
-func NewCmdCreate() *cobra.Command {
+func NewCmdCreate(flags *genericclioptions.ConfigFlags) *cobra.Command {
cmd := &cobra.Command{Use: "create", Short: "Create starter resources"}
cmd.AddCommand(
&cobra.Command{
@@ -26,7 +27,29 @@ func NewCmdCreate() *cobra.Command {
Use: "osupgrade",
Short: "Print an OSUpgrade template",
RunE: func(cmd *cobra.Command, _ []string) error {
- out, err := render.RenderOSUpgrade()
+ ns, _, err := flags.ToRawKubeConfigLoader().Namespace()
+ if err != nil {
+ return err
+ }
+
+ out, err := render.RenderOSUpgrade(ns)
+ if err != nil {
+ return err
+ }
+ _, err = fmt.Fprint(cmd.OutOrStdout(), out)
+ return err
+ },
+ },
+ &cobra.Command{
+ Use: "controller",
+ Short: "Print controller deployment template",
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ ns, _, err := flags.ToRawKubeConfigLoader().Namespace()
+ if err != nil {
+ return err
+ }
+
+ out, err := render.RenderControllerDeployments(ns)
if err != nil {
return err
}
diff --git a/clitools/pkg/cmd/root/root.go b/clitools/pkg/cmd/root/root.go
index 2b82b34..ce03222 100644
--- a/clitools/pkg/cmd/root/root.go
+++ b/clitools/pkg/cmd/root/root.go
@@ -44,7 +44,7 @@ func NewRootCmd() *cobra.Command {
versioncmd.NewCmdVersion(),
initcmd.NewCmdInit(flags),
checkconfigcmd.NewCmdCheckConfig(),
- createcmd.NewCmdCreate(),
+ createcmd.NewCmdCreate(flags),
agentcmd.NewCmdAgent(flags),
controllercmd.NewCmdController(flags),
internalcmd.NewCmdInternal(),
diff --git a/clitools/pkg/controller/server.go b/clitools/pkg/controller/server.go
index f35b149..5711bd7 100644
--- a/clitools/pkg/controller/server.go
+++ b/clitools/pkg/controller/server.go
@@ -76,6 +76,10 @@ func (s *Server) Initialize() {
ws.Route(ws.GET("/healthz").To(s.queryHealthz).
Doc("Return basic controller status"))
+ // Stub for now
+ ws.Route(ws.GET("/readyz").To(s.queryHealthz).
+ Doc("Stub for now"))
+
s.restfulCont.Add(ws)
}
diff --git a/clitools/pkg/node/agent.go b/clitools/pkg/node/agent.go
index 1f54c5f..aec8b75 100644
--- a/clitools/pkg/node/agent.go
+++ b/clitools/pkg/node/agent.go
@@ -20,7 +20,6 @@ import (
)
const (
- controlAgentName = "control-agent"
controlAgentNodeSelectorValue = "true"
controlAgentImage = "localhost/monok8s/control-agent:dev"
kubeconfig = "/etc/kubernetes/admin.conf"
@@ -50,7 +49,7 @@ func ApplyControlAgentDaemonSetResources(ctx context.Context, n *NodeContext) er
}
labels := map[string]string{
- "app.kubernetes.io/name": controlAgentName,
+ "app.kubernetes.io/name": monov1alpha1.ControlAgentName,
"app.kubernetes.io/component": "agent",
"app.kubernetes.io/part-of": "monok8s",
"app.kubernetes.io/managed-by": "ctl",
@@ -120,13 +119,13 @@ func copyStringMap(in map[string]string) map[string]string {
func applyControlAgentServiceAccount(ctx context.Context, kubeClient kubernetes.Interface, namespace string, labels map[string]string) error {
want := &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
- Name: controlAgentName,
+ Name: monov1alpha1.ControlAgentName,
Namespace: namespace,
Labels: labels,
},
}
- existing, err := kubeClient.CoreV1().ServiceAccounts(namespace).Get(ctx, controlAgentName, metav1.GetOptions{})
+ existing, err := kubeClient.CoreV1().ServiceAccounts(namespace).Get(ctx, monov1alpha1.ControlAgentName, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
_, err = kubeClient.CoreV1().ServiceAccounts(namespace).Create(ctx, want, metav1.CreateOptions{})
return err
@@ -175,13 +174,13 @@ func applyControlAgentClusterRole(ctx context.Context, kubeClient kubernetes.Int
want := &rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
- Name: controlAgentName,
+ Name: monov1alpha1.ControlAgentName,
Labels: labels,
},
Rules: wantRules,
}
- existing, err := kubeClient.RbacV1().ClusterRoles().Get(ctx, controlAgentName, metav1.GetOptions{})
+ existing, err := kubeClient.RbacV1().ClusterRoles().Get(ctx, monov1alpha1.ControlAgentName, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
_, err = kubeClient.RbacV1().ClusterRoles().Create(ctx, want, metav1.CreateOptions{})
return err
@@ -212,26 +211,26 @@ func applyControlAgentClusterRoleBinding(ctx context.Context, kubeClient kuberne
wantRoleRef := rbacv1.RoleRef{
APIGroup: rbacv1.GroupName,
Kind: "ClusterRole",
- Name: controlAgentName,
+ Name: monov1alpha1.ControlAgentName,
}
wantSubjects := []rbacv1.Subject{
{
Kind: "ServiceAccount",
- Name: controlAgentName,
+ Name: monov1alpha1.ControlAgentName,
Namespace: namespace,
},
}
want := &rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
- Name: controlAgentName,
+ Name: monov1alpha1.ControlAgentName,
Labels: labels,
},
RoleRef: wantRoleRef,
Subjects: wantSubjects,
}
- existing, err := kubeClient.RbacV1().ClusterRoleBindings().Get(ctx, controlAgentName, metav1.GetOptions{})
+ existing, err := kubeClient.RbacV1().ClusterRoleBindings().Get(ctx, monov1alpha1.ControlAgentName, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
_, err = kubeClient.RbacV1().ClusterRoleBindings().Create(ctx, want, metav1.CreateOptions{})
return err
@@ -242,7 +241,7 @@ func applyControlAgentClusterRoleBinding(ctx context.Context, kubeClient kuberne
// roleRef is immutable. If it differs, fail loudly instead of pretending we can patch it.
if !reflect.DeepEqual(existing.RoleRef, want.RoleRef) {
- return fmt.Errorf("existing ClusterRoleBinding %q has different roleRef and must be recreated", controlAgentName)
+ return fmt.Errorf("existing ClusterRoleBinding %q has different roleRef and must be recreated", monov1alpha1.ControlAgentName)
}
changed := false
@@ -267,7 +266,7 @@ func applyControlAgentDaemonSet(ctx context.Context, kubeClient kubernetes.Inter
privileged := true
dsLabels := map[string]string{
- "app.kubernetes.io/name": controlAgentName,
+ "app.kubernetes.io/name": monov1alpha1.ControlAgentName,
"app.kubernetes.io/component": "agent",
"app.kubernetes.io/part-of": "monok8s",
"app.kubernetes.io/managed-by": "ctl",
@@ -275,14 +274,14 @@ func applyControlAgentDaemonSet(ctx context.Context, kubeClient kubernetes.Inter
want := &appsv1.DaemonSet{
ObjectMeta: metav1.ObjectMeta{
- Name: controlAgentName,
+ Name: monov1alpha1.ControlAgentName,
Namespace: namespace,
Labels: labels,
},
Spec: appsv1.DaemonSetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
- "app.kubernetes.io/name": controlAgentName,
+ "app.kubernetes.io/name": monov1alpha1.ControlAgentName,
},
},
Template: corev1.PodTemplateSpec{
@@ -290,7 +289,7 @@ func applyControlAgentDaemonSet(ctx context.Context, kubeClient kubernetes.Inter
Labels: dsLabels,
},
Spec: corev1.PodSpec{
- ServiceAccountName: controlAgentName,
+ ServiceAccountName: monov1alpha1.ControlAgentName,
HostNetwork: true,
HostPID: true,
DNSPolicy: corev1.DNSClusterFirstWithHostNet,
@@ -380,7 +379,7 @@ func applyControlAgentDaemonSet(ctx context.Context, kubeClient kubernetes.Inter
},
}
- existing, err := kubeClient.AppsV1().DaemonSets(namespace).Get(ctx, controlAgentName, metav1.GetOptions{})
+ existing, err := kubeClient.AppsV1().DaemonSets(namespace).Get(ctx, monov1alpha1.ControlAgentName, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
_, err = kubeClient.AppsV1().DaemonSets(namespace).Create(ctx, want, metav1.CreateOptions{})
return err
diff --git a/clitools/pkg/node/controller.go b/clitools/pkg/node/controller.go
deleted file mode 100644
index 9e4c7dd..0000000
--- a/clitools/pkg/node/controller.go
+++ /dev/null
@@ -1,405 +0,0 @@
-package node
-
-import (
- "context"
- "fmt"
- "reflect"
- "sort"
- "strings"
-
- appsv1 "k8s.io/api/apps/v1"
- corev1 "k8s.io/api/core/v1"
- rbacv1 "k8s.io/api/rbac/v1"
- apierrors "k8s.io/apimachinery/pkg/api/errors"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/util/intstr"
- "k8s.io/client-go/kubernetes"
- "k8s.io/klog/v2"
-
- monov1alpha1 "example.com/monok8s/pkg/apis/monok8s/v1alpha1"
- "example.com/monok8s/pkg/kube"
- templates "example.com/monok8s/pkg/templates"
-)
-
-func applyControllerDeploymentResources(ctx context.Context, n *NodeContext) error {
- if strings.TrimSpace(n.Config.Spec.ClusterRole) != "control-plane" || !n.Config.Spec.EnableControlAgent {
- klog.InfoS("skipped controller deployment",
- "clusterRole", n.Config.Spec.ClusterRole,
- "enableControlAgent", n.Config.Spec.EnableControlAgent,
- )
- return nil
- }
-
- if err := ApplyCRDs(ctx, n); err != nil {
- return err
- }
-
- namespace := strings.TrimSpace(n.Config.Namespace)
- if namespace == "" {
- namespace = templates.DefaultNamespace
- }
-
- clients, err := kube.NewClientsFromKubeconfig(adminKubeconfigPath)
- if err != nil {
- return fmt.Errorf("build kube clients from %s: %w", adminKubeconfigPath, err)
- }
-
- labels := map[string]string{
- "app.kubernetes.io/name": controlAgentName,
- "app.kubernetes.io/component": "controller",
- "app.kubernetes.io/part-of": "monok8s",
- "app.kubernetes.io/managed-by": "ctl",
- }
-
- kubeClient := clients.Kubernetes
-
- if err := ensureNamespace(ctx, kubeClient, namespace, labels); err != nil {
- return fmt.Errorf("ensure namespace %q: %w", namespace, err)
- }
- if err := applyControllerServiceAccount(ctx, kubeClient, namespace, labels); err != nil {
- return fmt.Errorf("apply serviceaccount: %w", err)
- }
- if err := applyControllerClusterRole(ctx, kubeClient, labels); err != nil {
- return fmt.Errorf("apply clusterrole: %w", err)
- }
- if err := applyControllerClusterRoleBinding(ctx, kubeClient, namespace, labels); err != nil {
- return fmt.Errorf("apply clusterrolebinding: %w", err)
- }
- if err := applyControllerDeployment(ctx, kubeClient, namespace, labels); err != nil {
- return fmt.Errorf("apply deployment: %w", err)
- }
-
- return nil
-}
-
-func applyControllerServiceAccount(ctx context.Context, kubeClient kubernetes.Interface, namespace string, labels map[string]string) error {
- automount := true
-
- want := &corev1.ServiceAccount{
- ObjectMeta: metav1.ObjectMeta{
- Name: controlAgentName,
- Namespace: namespace,
- Labels: labels,
- },
- AutomountServiceAccountToken: &automount,
- }
-
- existing, err := kubeClient.CoreV1().ServiceAccounts(namespace).Get(ctx, controlAgentName, metav1.GetOptions{})
- if apierrors.IsNotFound(err) {
- _, err = kubeClient.CoreV1().ServiceAccounts(namespace).Create(ctx, want, metav1.CreateOptions{})
- return err
- }
- if err != nil {
- return err
- }
-
- changed := false
- if !reflect.DeepEqual(existing.Labels, want.Labels) {
- existing.Labels = want.Labels
- changed = true
- }
- if !reflect.DeepEqual(existing.AutomountServiceAccountToken, want.AutomountServiceAccountToken) {
- existing.AutomountServiceAccountToken = want.AutomountServiceAccountToken
- changed = true
- }
-
- if !changed {
- return nil
- }
-
- _, err = kubeClient.CoreV1().ServiceAccounts(namespace).Update(ctx, existing, metav1.UpdateOptions{})
- return err
-}
-
-func applyControllerClusterRole(ctx context.Context, kubeClient kubernetes.Interface, labels map[string]string) error {
- wantRules := []rbacv1.PolicyRule{
- {
- APIGroups: []string{monov1alpha1.Group},
- Resources: []string{"osupgrades"},
- Verbs: []string{"get", "list", "watch"},
- },
- {
- APIGroups: []string{monov1alpha1.Group},
- Resources: []string{"osupgrades/status"},
- Verbs: []string{"get", "patch", "update"},
- },
- {
- APIGroups: []string{monov1alpha1.Group},
- Resources: []string{"osupgradeprogresses"},
- Verbs: []string{"get", "list", "create"},
- },
- {
- APIGroups: []string{monov1alpha1.Group},
- Resources: []string{"osupgradeprogresses/status"},
- Verbs: []string{"create"},
- },
- {
- APIGroups: []string{""},
- Resources: []string{"nodes"},
- Verbs: []string{"get", "list", "watch"},
- },
- }
-
- want := &rbacv1.ClusterRole{
- ObjectMeta: metav1.ObjectMeta{
- Name: controlAgentName,
- Labels: labels,
- },
- Rules: wantRules,
- }
-
- existing, err := kubeClient.RbacV1().ClusterRoles().Get(ctx, controlAgentName, metav1.GetOptions{})
- if apierrors.IsNotFound(err) {
- _, err = kubeClient.RbacV1().ClusterRoles().Create(ctx, want, metav1.CreateOptions{})
- return err
- }
- if err != nil {
- return err
- }
-
- changed := false
- if !reflect.DeepEqual(existing.Labels, want.Labels) {
- existing.Labels = want.Labels
- changed = true
- }
- if !reflect.DeepEqual(existing.Rules, want.Rules) {
- existing.Rules = want.Rules
- changed = true
- }
-
- if !changed {
- return nil
- }
-
- _, err = kubeClient.RbacV1().ClusterRoles().Update(ctx, existing, metav1.UpdateOptions{})
- return err
-}
-
-func applyControllerClusterRoleBinding(ctx context.Context, kubeClient kubernetes.Interface, namespace string, labels map[string]string) error {
- wantSubjects := []rbacv1.Subject{
- {
- Kind: "ServiceAccount",
- Name: controlAgentName,
- Namespace: namespace,
- },
- }
-
- wantRoleRef := rbacv1.RoleRef{
- APIGroup: rbacv1.GroupName,
- Kind: "ClusterRole",
- Name: controlAgentName,
- }
-
- want := &rbacv1.ClusterRoleBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: controlAgentName,
- Labels: labels,
- },
- Subjects: wantSubjects,
- RoleRef: wantRoleRef,
- }
-
- existing, err := kubeClient.RbacV1().ClusterRoleBindings().Get(ctx, controlAgentName, metav1.GetOptions{})
- if apierrors.IsNotFound(err) {
- _, err = kubeClient.RbacV1().ClusterRoleBindings().Create(ctx, want, metav1.CreateOptions{})
- return err
- }
- if err != nil {
- return err
- }
-
- changed := false
- if !reflect.DeepEqual(existing.Labels, want.Labels) {
- existing.Labels = want.Labels
- changed = true
- }
- if !reflect.DeepEqual(existing.Subjects, want.Subjects) {
- existing.Subjects = want.Subjects
- changed = true
- }
- if !reflect.DeepEqual(existing.RoleRef, want.RoleRef) {
- existing.RoleRef = want.RoleRef
- changed = true
- }
-
- if !changed {
- return nil
- }
-
- _, err = kubeClient.RbacV1().ClusterRoleBindings().Update(ctx, existing, metav1.UpdateOptions{})
- return err
-}
-
-func applyControllerDeployment(ctx context.Context, kubeClient kubernetes.Interface, namespace string, labels map[string]string) error {
- replicas := int32(1)
-
- selectorLabels := map[string]string{
- "app.kubernetes.io/name": controlAgentName,
- "app.kubernetes.io/component": "controller",
- }
-
- podLabels := mergeStringMaps(labels, selectorLabels)
-
- runAsNonRoot := true
- allowPrivilegeEscalation := false
-
- want := &appsv1.Deployment{
- ObjectMeta: metav1.ObjectMeta{
- Name: controlAgentName,
- Namespace: namespace,
- Labels: labels,
- },
- Spec: appsv1.DeploymentSpec{
- Replicas: &replicas,
- Selector: &metav1.LabelSelector{
- MatchLabels: selectorLabels,
- },
- Template: corev1.PodTemplateSpec{
- ObjectMeta: metav1.ObjectMeta{
- Labels: podLabels,
- },
- Spec: corev1.PodSpec{
- ServiceAccountName: controlAgentName,
- Containers: []corev1.Container{
- {
- Name: "controller",
- Image: controlAgentImage,
- ImagePullPolicy: corev1.PullIfNotPresent,
- Args: []string{
- "controller",
- },
- Env: []corev1.EnvVar{
- {
- Name: "POD_NAME",
- ValueFrom: &corev1.EnvVarSource{
- FieldRef: &corev1.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "metadata.name",
- },
- },
- },
- {
- Name: "POD_NAMESPACE",
- ValueFrom: &corev1.EnvVarSource{
- FieldRef: &corev1.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "metadata.namespace",
- },
- },
- },
- {
- Name: "NODE_NAME",
- ValueFrom: &corev1.EnvVarSource{
- FieldRef: &corev1.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "spec.nodeName",
- },
- },
- },
- },
- Ports: []corev1.ContainerPort{
- {
- Name: "http",
- ContainerPort: 8080,
- Protocol: corev1.ProtocolTCP,
- },
- },
- LivenessProbe: &corev1.Probe{
- ProbeHandler: corev1.ProbeHandler{
- HTTPGet: &corev1.HTTPGetAction{
- Path: "/healthz",
- Port: intstr.FromString("http"),
- },
- },
- },
- ReadinessProbe: &corev1.Probe{
- ProbeHandler: corev1.ProbeHandler{
- HTTPGet: &corev1.HTTPGetAction{
- Path: "/readyz",
- Port: intstr.FromString("http"),
- },
- },
- },
- SecurityContext: &corev1.SecurityContext{
- RunAsNonRoot: &runAsNonRoot,
- AllowPrivilegeEscalation: &allowPrivilegeEscalation,
- },
- },
- },
- },
- },
- },
- }
-
- existing, err := kubeClient.AppsV1().Deployments(namespace).Get(ctx, controlAgentName, metav1.GetOptions{})
- if apierrors.IsNotFound(err) {
- _, err = kubeClient.AppsV1().Deployments(namespace).Create(ctx, want, metav1.CreateOptions{})
- return err
- }
- if err != nil {
- return err
- }
-
- changed := false
- if !reflect.DeepEqual(existing.Labels, want.Labels) {
- existing.Labels = want.Labels
- changed = true
- }
- if !reflect.DeepEqual(existing.Spec.Replicas, want.Spec.Replicas) {
- existing.Spec.Replicas = want.Spec.Replicas
- changed = true
- }
- if !reflect.DeepEqual(existing.Spec.Selector, want.Spec.Selector) {
- existing.Spec.Selector = want.Spec.Selector
- changed = true
- }
- if !reflect.DeepEqual(existing.Spec.Template.Labels, want.Spec.Template.Labels) {
- existing.Spec.Template.Labels = want.Spec.Template.Labels
- changed = true
- }
- if !reflect.DeepEqual(existing.Spec.Template.Spec.ServiceAccountName, want.Spec.Template.Spec.ServiceAccountName) {
- existing.Spec.Template.Spec.ServiceAccountName = want.Spec.Template.Spec.ServiceAccountName
- changed = true
- }
- if !reflect.DeepEqual(existing.Spec.Template.Spec.Containers, want.Spec.Template.Spec.Containers) {
- existing.Spec.Template.Spec.Containers = want.Spec.Template.Spec.Containers
- changed = true
- }
-
- if !changed {
- return nil
- }
-
- _, err = kubeClient.AppsV1().Deployments(namespace).Update(ctx, existing, metav1.UpdateOptions{})
- return err
-}
-
-func mergeStringMaps(maps ...map[string]string) map[string]string {
- var total int
- for _, m := range maps {
- total += len(m)
- }
- if total == 0 {
- return nil
- }
-
- out := make(map[string]string, total)
- for _, m := range maps {
- for k, v := range m {
- out[k] = v
- }
- }
- return out
-}
-
-func sortedKeys(m map[string]string) []string {
- if len(m) == 0 {
- return nil
- }
- keys := make([]string, 0, len(m))
- for k := range m {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- return keys
-}
diff --git a/clitools/pkg/render/controller.go b/clitools/pkg/render/controller.go
new file mode 100644
index 0000000..c353e3f
--- /dev/null
+++ b/clitools/pkg/render/controller.go
@@ -0,0 +1,278 @@
+package render
+
+import (
+ "bytes"
+ "fmt"
+
+ appsv1 "k8s.io/api/apps/v1"
+ corev1 "k8s.io/api/core/v1"
+ rbacv1 "k8s.io/api/rbac/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/serializer/json"
+ "k8s.io/apimachinery/pkg/util/intstr"
+
+ monov1alpha1 "example.com/monok8s/pkg/apis/monok8s/v1alpha1"
+ templates "example.com/monok8s/pkg/templates"
+)
+
+func RenderControllerDeployments(namespace string) (string, error) {
+ vals := templates.LoadTemplateValuesFromEnv()
+
+ labels := map[string]string{
+ "app.kubernetes.io/name": monov1alpha1.ControlAgentName,
+ "app.kubernetes.io/component": "controller",
+ "app.kubernetes.io/part-of": "monok8s",
+ "app.kubernetes.io/managed-by": "ctl",
+ }
+
+ objs := []runtime.Object{
+ buildControllerServiceAccount(namespace, labels),
+ buildControllerClusterRole(labels),
+ buildControllerClusterRoleBinding(namespace, labels),
+ buildControllerDeployment(vals, namespace, labels),
+ }
+
+ s := runtime.NewScheme()
+ _ = corev1.AddToScheme(s)
+ _ = rbacv1.AddToScheme(s)
+ _ = appsv1.AddToScheme(s)
+
+ serializer := json.NewYAMLSerializer(json.DefaultMetaFactory, s, s)
+
+ var buf bytes.Buffer
+
+ for i, obj := range objs {
+ if i > 0 {
+ if _, err := fmt.Fprintln(&buf, "---"); err != nil {
+ return "", err
+ }
+ }
+ if err := serializer.Encode(obj, &buf); err != nil {
+ return "", err
+ }
+ }
+
+ return buf.String(), nil
+}
+
+func buildControllerServiceAccount(namespace string, labels map[string]string) *corev1.ServiceAccount {
+
+ automount := true
+
+ return &corev1.ServiceAccount{
+ TypeMeta: metav1.TypeMeta{
+ APIVersion: "v1",
+ Kind: "ServiceAccount",
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Name: monov1alpha1.ControlAgentName,
+ Namespace: namespace,
+ Labels: labels,
+ },
+ AutomountServiceAccountToken: &automount,
+ }
+}
+
+func buildControllerClusterRole(labels map[string]string) *rbacv1.ClusterRole {
+ wantRules := []rbacv1.PolicyRule{
+ {
+ APIGroups: []string{monov1alpha1.Group},
+ Resources: []string{"osupgrades"},
+ Verbs: []string{"get", "list", "watch"},
+ },
+ {
+ APIGroups: []string{monov1alpha1.Group},
+ Resources: []string{"osupgrades/status"},
+ Verbs: []string{"get", "patch", "update"},
+ },
+ {
+ APIGroups: []string{monov1alpha1.Group},
+ Resources: []string{"osupgradeprogresses"},
+ Verbs: []string{"get", "create"},
+ },
+ {
+ APIGroups: []string{monov1alpha1.Group},
+ Resources: []string{"osupgradeprogresses/status"},
+ Verbs: []string{"update"},
+ },
+ {
+ APIGroups: []string{""},
+ Resources: []string{"nodes"},
+ Verbs: []string{"get", "list"},
+ },
+ }
+
+ return &rbacv1.ClusterRole{
+ TypeMeta: metav1.TypeMeta{
+ APIVersion: "rbac.authorization.k8s.io/v1",
+ Kind: "ClusterRole",
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Name: monov1alpha1.ControlAgentName,
+ Labels: labels,
+ },
+ Rules: wantRules,
+ }
+}
+
+func buildControllerClusterRoleBinding(namespace string, labels map[string]string) *rbacv1.ClusterRoleBinding {
+
+ wantSubjects := []rbacv1.Subject{
+ {
+ Kind: "ServiceAccount",
+ Name: monov1alpha1.ControlAgentName,
+ Namespace: namespace,
+ },
+ }
+
+ wantRoleRef := rbacv1.RoleRef{
+ APIGroup: rbacv1.GroupName,
+ Kind: "ClusterRole",
+ Name: monov1alpha1.ControlAgentName,
+ }
+
+ return &rbacv1.ClusterRoleBinding{
+ TypeMeta: metav1.TypeMeta{
+ APIVersion: "rbac.authorization.k8s.io/v1",
+ Kind: "ClusterRoleBinding",
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Name: monov1alpha1.ControlAgentName,
+ Labels: labels,
+ },
+ Subjects: wantSubjects,
+ RoleRef: wantRoleRef,
+ }
+}
+
+func buildControllerDeployment(tVals templates.TemplateValues, namespace string, labels map[string]string) *appsv1.Deployment {
+ replicas := int32(1)
+
+ selectorLabels := map[string]string{
+ "app.kubernetes.io/name": monov1alpha1.ControlAgentName,
+ "app.kubernetes.io/component": "controller",
+ }
+
+ podLabels := mergeStringMaps(labels, selectorLabels)
+
+ runAsNonRoot := true
+ allowPrivilegeEscalation := false
+
+ return &appsv1.Deployment{
+ TypeMeta: metav1.TypeMeta{
+ APIVersion: "apps/v1",
+ Kind: "Deployment",
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Name: monov1alpha1.ControlAgentName,
+ Namespace: namespace,
+ Labels: labels,
+ },
+ Spec: appsv1.DeploymentSpec{
+ Replicas: &replicas,
+ Selector: &metav1.LabelSelector{
+ MatchLabels: selectorLabels,
+ },
+ Template: corev1.PodTemplateSpec{
+ ObjectMeta: metav1.ObjectMeta{
+ Labels: podLabels,
+ },
+ Spec: corev1.PodSpec{
+ ServiceAccountName: monov1alpha1.ControlAgentName,
+ Containers: []corev1.Container{
+ {
+ Name: "controller",
+ Image: fmt.Sprintf("registry.local/control-agent:%s", tVals.KubernetesVersion),
+ ImagePullPolicy: corev1.PullIfNotPresent,
+ Args: []string{
+ "controller",
+ "--namespace",
+ namespace,
+ },
+ Env: []corev1.EnvVar{
+ {
+ Name: "POD_NAME",
+ ValueFrom: &corev1.EnvVarSource{
+ FieldRef: &corev1.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "metadata.name",
+ },
+ },
+ },
+ {
+ Name: "POD_NAMESPACE",
+ ValueFrom: &corev1.EnvVarSource{
+ FieldRef: &corev1.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "metadata.namespace",
+ },
+ },
+ },
+ {
+ Name: "NODE_NAME",
+ ValueFrom: &corev1.EnvVarSource{
+ FieldRef: &corev1.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "spec.nodeName",
+ },
+ },
+ },
+ },
+ Ports: []corev1.ContainerPort{
+ {
+ Name: "http",
+ ContainerPort: 8080,
+ Protocol: corev1.ProtocolTCP,
+ },
+ {
+ Name: "https",
+ ContainerPort: 8443,
+ Protocol: corev1.ProtocolTCP,
+ },
+ },
+ LivenessProbe: &corev1.Probe{
+ ProbeHandler: corev1.ProbeHandler{
+ HTTPGet: &corev1.HTTPGetAction{
+ Path: "/healthz",
+ Port: intstr.FromString("http"),
+ },
+ },
+ },
+ ReadinessProbe: &corev1.Probe{
+ ProbeHandler: corev1.ProbeHandler{
+ HTTPGet: &corev1.HTTPGetAction{
+ Path: "/readyz",
+ Port: intstr.FromString("http"),
+ },
+ },
+ },
+ SecurityContext: &corev1.SecurityContext{
+ RunAsNonRoot: &runAsNonRoot,
+ AllowPrivilegeEscalation: &allowPrivilegeEscalation,
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func mergeStringMaps(maps ...map[string]string) map[string]string {
+ var total int
+ for _, m := range maps {
+ total += len(m)
+ }
+ if total == 0 {
+ return nil
+ }
+
+ out := make(map[string]string, total)
+ for _, m := range maps {
+ for k, v := range m {
+ out[k] = v
+ }
+ }
+ return out
+}
diff --git a/clitools/pkg/render/monoks.go b/clitools/pkg/render/monoks.go
index 21d5e35..b0bee07 100644
--- a/clitools/pkg/render/monoks.go
+++ b/clitools/pkg/render/monoks.go
@@ -1,4 +1,4 @@
-package templates
+package render
import (
"bytes"
@@ -31,10 +31,12 @@ func RenderMonoKSConfig() (string, error) {
return buf.String(), nil
}
-func RenderOSUpgrade() (string, error) {
+func RenderOSUpgrade(namespace string) (string, error) {
vals := templates.LoadTemplateValuesFromEnv()
cfg := templates.DefaultOSUpgrade(vals)
+ cfg.Namespace = namespace
+
s := runtime.NewScheme()
if err := monov1alpha1.AddToScheme(s); err != nil {
return "", err