280 lines
6.8 KiB
Go
280 lines
6.8 KiB
Go
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"
|
|
buildinfo "example.com/monok8s/pkg/buildinfo"
|
|
templates "example.com/monok8s/pkg/templates"
|
|
)
|
|
|
|
func RenderControllerDeployments(namespace string) (string, error) {
|
|
vals := templates.LoadTemplateValuesFromEnv()
|
|
|
|
labels := map[string]string{
|
|
"app.kubernetes.io/name": monov1alpha1.ControllerName,
|
|
"app.kubernetes.io/component": "controller",
|
|
"app.kubernetes.io/part-of": "monok8s",
|
|
"app.kubernetes.io/managed-by": monov1alpha1.NodeControlName,
|
|
}
|
|
|
|
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.ControllerName,
|
|
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.ControllerName,
|
|
Labels: labels,
|
|
},
|
|
Rules: wantRules,
|
|
}
|
|
}
|
|
|
|
func buildControllerClusterRoleBinding(namespace string, labels map[string]string) *rbacv1.ClusterRoleBinding {
|
|
|
|
wantSubjects := []rbacv1.Subject{
|
|
{
|
|
Kind: "ServiceAccount",
|
|
Name: monov1alpha1.ControllerName,
|
|
Namespace: namespace,
|
|
},
|
|
}
|
|
|
|
wantRoleRef := rbacv1.RoleRef{
|
|
APIGroup: rbacv1.GroupName,
|
|
Kind: "ClusterRole",
|
|
Name: monov1alpha1.ControllerName,
|
|
}
|
|
|
|
return &rbacv1.ClusterRoleBinding{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "rbac.authorization.k8s.io/v1",
|
|
Kind: "ClusterRoleBinding",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: monov1alpha1.ControllerName,
|
|
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.ControllerName,
|
|
"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.ControllerName,
|
|
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.ControllerName,
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "controller",
|
|
Image: fmt.Sprintf("localhost/monok8s/node-control:%s", buildinfo.Version),
|
|
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
|
|
}
|