airshipctl/pkg/workflow/initialize.go
2019-06-05 10:04:43 -05:00

393 lines
9.5 KiB
Go

package workflow
import (
"fmt"
"io"
v1beta2 "k8s.io/api/apps/v1beta2"
"k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
apixv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
apixv1beta1client "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"github.com/ian-howell/airshipctl/pkg/workflow/environment"
)
const (
argoNamespace = "argo"
)
// Initialize creates the argo namespace and all of the required kubernetes
// objects for argo to function
func Initialize(out io.Writer, settings *environment.Settings, manifestPath string) error {
kubeClient := settings.KubeClient
crdClient := settings.CRDClient
if err := createNamespace(out, kubeClient); err != nil {
return err
}
if manifestPath == "" {
if err := createDefaultObjects(out, kubeClient, crdClient); err != nil {
return fmt.Errorf("could not create default objects: %s", err.Error())
}
} else {
if err := createCustomObjects(manifestPath); err != nil {
return fmt.Errorf("could not create objects: %s", err.Error())
}
}
return nil
}
func createNamespace(out io.Writer, kubeClient kubernetes.Interface) error {
fmt.Fprintf(out, "Creating namespace %s\n", argoNamespace)
_, err := kubeClient.CoreV1().Namespaces().Create(&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "argo"}})
return handleCreateError(out, fmt.Sprintf("namespace %s", argoNamespace), err)
}
func createDefaultObjects(out io.Writer, kubeClient kubernetes.Interface, crdClient apixv1beta1client.Interface) error {
fmt.Fprintf(out, "Registering Workflow CRD\n")
if err := handleCreateError(out, "Workflow CRD", registerDefaultWorkflow(crdClient)); err != nil {
return err
}
fmt.Fprintf(out, "Creating argo ServiceAccount\n")
if err := handleCreateError(out, "argo ServiceAccount", createArgoServiceAccount(kubeClient)); err != nil {
return err
}
fmt.Fprintf(out, "Creating argo admin ClusterRole\n")
if err := handleCreateError(out, "argo admin ClusterRole", createArgoAdminClusterRole(kubeClient)); err != nil {
return err
}
fmt.Fprintf(out, "Creating argo edit ClusterRole\n")
if err := handleCreateError(out, "argo edit ClusterRole", createArgoEditClusterRole(kubeClient)); err != nil {
return err
}
fmt.Fprintf(out, "Creating argo view ClusterRole\n")
if err := handleCreateError(out, "argo view ClusterRole", createArgoViewClusterRole(kubeClient)); err != nil {
return err
}
fmt.Fprintf(out, "Creating argo ClusterRole\n")
if err := handleCreateError(out, "argo ClusterRole", createArgoClusterRole(kubeClient)); err != nil {
return err
}
fmt.Fprintf(out, "Creating argo ClusterRoleBinding\n")
if err := handleCreateError(out, "argo ClusterRoleBinding", createArgoClusterRoleBinding(kubeClient)); err != nil {
return err
}
fmt.Fprintf(out, "Creating argo ConfigMap\n")
if err := handleCreateError(out, "argo ConfigMap", createArgoConfigMap(kubeClient)); err != nil {
return err
}
fmt.Fprintf(out, "Creating argo Deployment\n")
if err := handleCreateError(out, "argo Deployment", createArgoDeployment(kubeClient)); err != nil {
return err
}
return nil
}
func createCustomObjects(manifestPath string) error {
//TODO
return nil
}
func registerDefaultWorkflow(crdClient apixv1beta1client.Interface) error {
_, err := crdClient.ApiextensionsV1beta1().CustomResourceDefinitions().
Create(&apixv1beta1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "workflows.argoproj.io",
},
Spec: apixv1beta1.CustomResourceDefinitionSpec{
Group: "argoproj.io",
Version: "v1alpha1",
Versions: []apixv1beta1.CustomResourceDefinitionVersion{{
Name: "v1alpha1",
Served: true,
Storage: true,
}},
Names: apixv1beta1.CustomResourceDefinitionNames{
Plural: "workflows",
Kind: "Workflow",
},
Scope: apixv1beta1.NamespaceScoped,
},
})
return err
}
func createArgoServiceAccount(kubeClient kubernetes.Interface) error {
_, err := kubeClient.CoreV1().ServiceAccounts(argoNamespace).
Create(&v1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: "argo",
Namespace: argoNamespace,
},
})
return err
}
func createArgoAdminClusterRole(kubeClient kubernetes.Interface) error {
_, err := kubeClient.RbacV1().ClusterRoles().
Create(&rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: "argo-aggregate-to-admin",
Labels: map[string]string{
"rbac.authorization.k8s.io/aggregate-to-admin": "true",
},
},
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{
"argoproj.io",
},
Resources: []string{
"workflows",
"workflows/finalizers",
},
Verbs: []string{
"create",
"delete",
"deletecollection",
"get",
"list",
"patch",
"update",
"watch",
},
},
},
})
return err
}
func createArgoEditClusterRole(kubeClient kubernetes.Interface) error {
_, err := kubeClient.RbacV1().ClusterRoles().
Create(&rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: "argo-aggregate-to-edit",
Labels: map[string]string{
"rbac.authorization.k8s.io/aggregate-to-edit": "true",
},
},
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{
"argoproj.io",
},
Resources: []string{
"workflows",
"workflows/finalizers",
},
Verbs: []string{
"create",
"delete",
"deletecollection",
"get",
"list",
"patch",
"update",
"watch",
},
},
},
})
return err
}
func createArgoViewClusterRole(kubeClient kubernetes.Interface) error {
_, err := kubeClient.RbacV1().ClusterRoles().
Create(&rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: "argo-aggregate-to-view",
Labels: map[string]string{
"rbac.authorization.k8s.io/aggregate-to-view": "true",
},
},
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{
"argoproj.io",
},
Resources: []string{
"workflows",
"workflows/finalizers",
},
Verbs: []string{
"get",
"list",
"watch",
},
},
},
})
return err
}
func createArgoClusterRole(kubeClient kubernetes.Interface) error {
_, err := kubeClient.RbacV1().ClusterRoles().
Create(&rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{Name: "argo-cluster-role"},
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{
"",
},
Resources: []string{
"pods",
"pods/exec",
},
Verbs: []string{
"create",
"delete",
"get",
"list",
"patch",
"update",
"watch",
},
},
{
APIGroups: []string{
"",
},
Resources: []string{
"configmaps",
},
Verbs: []string{
"get",
"list",
"watch",
},
},
{
APIGroups: []string{
"",
},
Resources: []string{
"persistentvolumeclaims",
},
Verbs: []string{
"create",
"delete",
},
},
{
APIGroups: []string{
"argoproj.io",
},
Resources: []string{
"workflows",
"workflows/finalizers",
},
Verbs: []string{
"delete",
"get",
"list",
"patch",
"update",
"watch",
},
},
},
})
return err
}
func createArgoClusterRoleBinding(kubeClient kubernetes.Interface) error {
_, err := kubeClient.RbacV1().ClusterRoleBindings().
Create(&rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{Name: "argo-binding"},
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "ClusterRole",
Name: "argo-cluster-role",
},
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
Name: "argo",
Namespace: argoNamespace,
},
},
})
return err
}
func createArgoConfigMap(kubeClient kubernetes.Interface) error {
_, err := kubeClient.CoreV1().ConfigMaps(argoNamespace).
Create(&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "workflow-controller-configmap",
Namespace: argoNamespace,
},
})
return err
}
func createArgoDeployment(kubeClient kubernetes.Interface) error {
_, err := kubeClient.AppsV1beta2().Deployments(argoNamespace).
Create(&v1beta2.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "workflow-controller",
Namespace: argoNamespace,
},
Spec: v1beta2.DeploymentSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "workflow-controller",
},
},
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": "workflow-controller",
},
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Command: []string{
"workflow-controller",
},
Args: []string{
"--configmap",
"workflow-controller-configmap",
"--executor-image",
"argoproj/argoexec:v2.2.1", // TODO(howell): Remove this hardcoded value
},
Image: "argoproj/argoexec:v2.2.1", // TODO(howell): Remove this hardcoded value
Name: "workflow-controller",
},
},
ServiceAccountName: "argo",
},
},
},
})
return err
}
func handleCreateError(out io.Writer, resourceName string, err error) error {
if err == nil {
return nil
}
if errors.IsAlreadyExists(err) {
fmt.Fprintf(out, "%s already exists\n", resourceName)
return nil
}
return fmt.Errorf("Could not create %s: %s", resourceName, err.Error())
}