
This uses the current master commit of golangci-lint. We will want to update to use a tagged version as soon as the golangci-lint team pushes a new tag
444 lines
11 KiB
Go
444 lines
11 KiB
Go
package workflow
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/spf13/cobra"
|
|
v1beta2 "k8s.io/api/apps/v1beta2"
|
|
v1 "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/typed/apiextensions/v1beta1"
|
|
"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/environment"
|
|
wfenv "github.com/ian-howell/airshipctl/pkg/workflow/environment"
|
|
)
|
|
|
|
const (
|
|
argoNamespace = "argo"
|
|
)
|
|
|
|
var (
|
|
manifestPath string
|
|
)
|
|
|
|
type workflowInitCmd struct {
|
|
out io.Writer
|
|
kubeclient kubernetes.Interface
|
|
crdclient apixv1beta1client.ApiextensionsV1beta1Interface
|
|
}
|
|
|
|
// NewWorkflowInitCommand is a command for bootstrapping a kubernetes cluster with the necessary components for Argo workflows
|
|
func NewWorkflowInitCommand(rootSettings *environment.AirshipCTLSettings) *cobra.Command {
|
|
|
|
workflowInitCommand := &cobra.Command{
|
|
Use: "init [flags]",
|
|
Short: "bootstraps the kubernetes cluster with the Workflow CRDs and controller",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
out := cmd.OutOrStdout()
|
|
wfSettings, ok := rootSettings.PluginSettings[PluginSettingsID].(*wfenv.Settings)
|
|
if !ok {
|
|
fmt.Fprintf(out, "settings for %s were not registered\n", PluginSettingsID)
|
|
return
|
|
}
|
|
|
|
workflowInit := &workflowInitCmd{
|
|
out: out,
|
|
kubeclient: wfSettings.KubeClient,
|
|
crdclient: wfSettings.CRDClient.ApiextensionsV1beta1(),
|
|
}
|
|
|
|
fmt.Fprintf(out, "Creating namespace \"%s\"\n", argoNamespace)
|
|
_, err := workflowInit.kubeclient.CoreV1().Namespaces().Create(&v1.Namespace{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "argo"},
|
|
})
|
|
if err != nil {
|
|
if errors.IsAlreadyExists(err) {
|
|
fmt.Fprintf(out, "Namespace \"%s\" already exists\n", argoNamespace)
|
|
} else {
|
|
fmt.Fprintf(out, "Could not create namespace \"%s\": %s\n", argoNamespace, err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
if manifestPath == "" {
|
|
if err := workflowInit.createDefaultObjects(); err != nil {
|
|
fmt.Fprintf(out, "Could not create default objects: %s\n", err.Error())
|
|
return
|
|
}
|
|
} else {
|
|
workflowInit.createCustomObjects(manifestPath)
|
|
}
|
|
|
|
},
|
|
}
|
|
|
|
workflowInitCommand.PersistentFlags().StringVar(&manifestPath, "manifest", "", "path to a YAML manifest containing definitions of objects needed for Argo workflows")
|
|
return workflowInitCommand
|
|
}
|
|
|
|
func (wfInit *workflowInitCmd) createDefaultObjects() error {
|
|
//TODO(howell): Clean up the repetitive code
|
|
fmt.Fprintf(wfInit.out, "Registering Workflow CRD\n")
|
|
if err := wfInit.registerDefaultWorkflow(); err != nil {
|
|
if errors.IsAlreadyExists(err) {
|
|
fmt.Fprintf(wfInit.out, "Workflow CRD already registered\n")
|
|
} else {
|
|
return fmt.Errorf("Could not register Workflow CRD: %s", err.Error())
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(wfInit.out, "Creating argo ServiceAccount\n")
|
|
if err := wfInit.createArgoServiceAccount(); err != nil {
|
|
if errors.IsAlreadyExists(err) {
|
|
fmt.Fprintf(wfInit.out, "argo ServiceAccount already exists\n")
|
|
} else {
|
|
return fmt.Errorf("Could not create argo ServiceAccount: %s", err.Error())
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(wfInit.out, "Creating argo admin ClusterRole\n")
|
|
if err := wfInit.createArgoAdminClusterRole(); err != nil {
|
|
if errors.IsAlreadyExists(err) {
|
|
fmt.Fprintf(wfInit.out, "argo admin ClusterRole already exists\n")
|
|
} else {
|
|
return fmt.Errorf("Could not create argo admin ClusterRole: %s", err.Error())
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(wfInit.out, "Creating argo edit ClusterRole\n")
|
|
if err := wfInit.createArgoEditClusterRole(); err != nil {
|
|
if errors.IsAlreadyExists(err) {
|
|
fmt.Fprintf(wfInit.out, "argo edit ClusterRole already exists\n")
|
|
} else {
|
|
return fmt.Errorf("Could not create argo edit ClusterRole: %s", err.Error())
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(wfInit.out, "Creating argo view ClusterRole\n")
|
|
if err := wfInit.createArgoViewClusterRole(); err != nil {
|
|
if errors.IsAlreadyExists(err) {
|
|
fmt.Fprintf(wfInit.out, "argo view ClusterRole already exists\n")
|
|
} else {
|
|
return fmt.Errorf("Could not create argo view ClusterRole: %s", err.Error())
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(wfInit.out, "Creating argo ClusterRole\n")
|
|
if err := wfInit.createArgoClusterRole(); err != nil {
|
|
if errors.IsAlreadyExists(err) {
|
|
fmt.Fprintf(wfInit.out, "argo ClusterRole already exists\n")
|
|
} else {
|
|
return fmt.Errorf("Could not create argo ClusterRole: %s", err.Error())
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(wfInit.out, "Creating argo ClusterRoleBinding\n")
|
|
if err := wfInit.createArgoClusterRoleBinding(); err != nil {
|
|
if errors.IsAlreadyExists(err) {
|
|
fmt.Fprintf(wfInit.out, "argo ClusterRoleBinding already exists\n")
|
|
} else {
|
|
return fmt.Errorf("Could not create argo ClusterRoleBinding: %s", err.Error())
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(wfInit.out, "Creating argo ConfigMap\n")
|
|
if err := wfInit.createArgoConfigMap(); err != nil {
|
|
if errors.IsAlreadyExists(err) {
|
|
fmt.Fprintf(wfInit.out, "argo ConfigMap already exists\n")
|
|
} else {
|
|
return fmt.Errorf("Could not create argo ConfigMap: %s", err.Error())
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(wfInit.out, "Creating argo Deployment\n")
|
|
if err := wfInit.createArgoDeployment(); err != nil {
|
|
if errors.IsAlreadyExists(err) {
|
|
fmt.Fprintf(wfInit.out, "argo Deployment already exists\n")
|
|
} else {
|
|
return fmt.Errorf("Could not create argo Deployment: %s", err.Error())
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (wfInit *workflowInitCmd) createCustomObjects(manifestPath string) {
|
|
//TODO
|
|
}
|
|
|
|
func (wfInit *workflowInitCmd) registerDefaultWorkflow() error {
|
|
apixClient := wfInit.crdclient
|
|
crds := apixClient.CustomResourceDefinitions()
|
|
workflowCRD := &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,
|
|
},
|
|
}
|
|
_, err := crds.Create(workflowCRD)
|
|
return err
|
|
}
|
|
|
|
func (wfInit *workflowInitCmd) createArgoServiceAccount() error {
|
|
_, err := wfInit.kubeclient.CoreV1().ServiceAccounts(argoNamespace).Create(&v1.ServiceAccount{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "argo",
|
|
Namespace: argoNamespace,
|
|
},
|
|
})
|
|
return err
|
|
}
|
|
|
|
func (wfInit *workflowInitCmd) createArgoAdminClusterRole() error {
|
|
_, err := wfInit.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 (wfInit *workflowInitCmd) createArgoEditClusterRole() error {
|
|
_, err := wfInit.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 (wfInit *workflowInitCmd) createArgoViewClusterRole() error {
|
|
_, err := wfInit.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 (wfInit *workflowInitCmd) createArgoClusterRole() error {
|
|
_, err := wfInit.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 (wfInit *workflowInitCmd) createArgoClusterRoleBinding() error {
|
|
_, err := wfInit.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 (wfInit *workflowInitCmd) createArgoConfigMap() error {
|
|
_, err := wfInit.kubeclient.CoreV1().ConfigMaps(argoNamespace).Create(&v1.ConfigMap{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "workflow-controller-configmap",
|
|
Namespace: argoNamespace,
|
|
},
|
|
})
|
|
return err
|
|
}
|
|
|
|
func (wfInit *workflowInitCmd) createArgoDeployment() error {
|
|
_, err := wfInit.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
|
|
}
|