From 6407c2a7b4c718b8982c7891a6f6a645e8549d35 Mon Sep 17 00:00:00 2001 From: Andrey Melnikov Date: Wed, 27 Oct 2021 09:24:28 -0700 Subject: [PATCH] feat: add create namespace code --- pkg/namespace.go | 438 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 436 insertions(+), 2 deletions(-) diff --git a/pkg/namespace.go b/pkg/namespace.go index 46a2921..099b11b 100644 --- a/pkg/namespace.go +++ b/pkg/namespace.go @@ -1,11 +1,20 @@ package v1 import ( + "encoding/base64" "fmt" "github.com/onepanelio/core/pkg/util" + "github.com/onepanelio/core/pkg/util/data" "google.golang.org/grpc/codes" "io/ioutil" + vapps "k8s.io/api/apps/v1" + v1 "k8s.io/api/core/v1" + vnet "k8s.io/api/networking/v1" + v1rbac "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/json" + "os" + "path/filepath" "strings" ) @@ -77,7 +86,432 @@ func (c *Client) ListNamespaces() (namespaces []*Namespace, err error) { return } -// CreateNamespace creates a new namespace in the system func (c *Client) CreateNamespace(sourceNamespace, name string) (namespace *Namespace, err error) { - return nil, util.NewUserError(codes.FailedPrecondition, "Creating namespaces is not supported in the community edition") + newNamespace := name + domain := *c.systemConfig.Domain() + artifactRepositorySource, err := c.GetArtifactRepositorySource(sourceNamespace) + if err != nil { + return nil, err + } + + config, err := c.GetNamespaceConfig(sourceNamespace) + if err != nil { + return nil, err + } + if config.ArtifactRepository.S3 == nil { + return nil, util.NewUserError(codes.Internal, "S3 compatible artifact repository not set") + } + + accessKey := config.ArtifactRepository.S3.AccessKey + secretKey := config.ArtifactRepository.S3.Secretkey + + if err := c.createK8sNamespace(name); err != nil { + return nil, err + } + + if err := c.createNetworkPolicy(newNamespace); err != nil { + return nil, err + } + + if err := c.createIstioVirtualService(newNamespace, domain); err != nil { + return nil, err + } + + if err := c.createRole(newNamespace); err != nil { + return nil, err + } + + if err := c.createDefaultSecret(newNamespace); err != nil { + return nil, err + } + + if err := c.createSecretOnepanelDefaultNamespace(newNamespace, accessKey, secretKey); err != nil { + return nil, err + } + + if err := c.createProviderDependentMinioDeployment(newNamespace, artifactRepositorySource); err != nil { + return nil, err + } + + if err := c.createProviderDependentMinioService(newNamespace, artifactRepositorySource); err != nil { + return nil, err + } + + if err := c.createNamespaceConfigMap(sourceNamespace, newNamespace); err != nil { + return nil, err + } + + if err := c.createNamespaceClusterRoleBinding(newNamespace); err != nil { + return nil, err + } + + if err := c.createNamespaceRoleBinding(newNamespace); err != nil { + return nil, err + } + + if err := c.createNamespaceServiceAccount(newNamespace); err != nil { + return nil, err + } + + if err := c.createNamespaceModelClusterRoleBinding(newNamespace); err != nil { + return nil, err + } + + if err := c.createNamespaceTemplates(newNamespace); err != nil { + return nil, err + } + + namespace = &Namespace{ + Name: name, + } + + return +} + +func (c *Client) createK8sNamespace(name string) error { + createNamespace := &v1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: map[string]string{ + "istio-injection": "enabled", + onepanelEnabledLabelKey: "true", + "app.kubernetes.io/component": "onepanel", + "app.kubernetes.io/instance": "onepanel-v0.5.0", + "app.kubernetes.io/managed-by": "onepanel-cli", + "app.kubernetes.io/name": "onepanel", + "app.kubernetes.io/part-of": "onepanel", + "app.kubernetes.io/version": "v0.5.0", + }, + }, + } + + _, err := c.CoreV1().Namespaces().Create(createNamespace) + if err != nil && strings.Contains(err.Error(), "already exists") { + return util.NewUserError(codes.AlreadyExists, "Namespace '"+name+"' already exists") + } + + return err +} + +func (c *Client) createNetworkPolicy(namespace string) error { + replacements := map[string]string{ + "$(applicationDefaultNamespace)": namespace, + } + + dataStr, err := replaceVariables(filepath.Join("manifest", "networkpolicy-onepanel-defaultnamespace.json"), replacements) + if err != nil { + return err + } + data := []byte(dataStr) + + networkPolicy := &vnet.NetworkPolicy{} + if err := json.Unmarshal(data, networkPolicy); err != nil { + return err + } + + _, err = c.NetworkingV1().NetworkPolicies(namespace).Create(networkPolicy) + + return err +} + +func (c *Client) createIstioVirtualService(namespace, domain string) error { + replacements := map[string]string{ + "$(applicationDefaultNamespace)": namespace, + "$(applicationDomain)": domain, + } + + dataStr, err := replaceVariables(filepath.Join("manifest", "service-minio-onepanel.json"), replacements) + if err != nil { + return err + } + data := []byte(dataStr) + + return c.CreateVirtualService(namespace, data) +} + +func (c *Client) createRole(namespace string) error { + replacements := map[string]string{ + "$(applicationDefaultNamespace)": namespace, + } + + dataStr, err := replaceVariables(filepath.Join("manifest", "role-onepanel-defaultnamespace.json"), replacements) + if err != nil { + return err + } + data := []byte(dataStr) + + role := &v1rbac.Role{} + if err := json.Unmarshal(data, role); err != nil { + return err + } + + _, err = c.RbacV1().Roles(namespace).Create(role) + + return err +} + +func (c *Client) createDefaultSecret(namespace string) error { + replacements := map[string]string{ + "$(applicationDefaultNamespace)": namespace, + } + + dataStr, err := replaceVariables(filepath.Join("manifest", "secret-onepanel-default-env-defaultnamespace.json"), replacements) + if err != nil { + return err + } + data := []byte(dataStr) + + secret := &v1.Secret{} + if err := json.Unmarshal(data, secret); err != nil { + return err + } + + _, err = c.CoreV1().Secrets(namespace).Create(secret) + + return err +} + +func (c *Client) createSecretOnepanelDefaultNamespace(namespace, accessKey, secretKey string) error { + replacements := map[string]string{ + "$(applicationDefaultNamespace)": namespace, + "$(artifactRepositoryS3AccessKey)": base64.StdEncoding.EncodeToString([]byte(accessKey)), + "$(artifactRepositoryS3SecretKey)": base64.StdEncoding.EncodeToString([]byte(secretKey)), + } + + dataStr, err := replaceVariables(filepath.Join("manifest", "secret-onepanel-defaultnamespace.json"), replacements) + if err != nil { + return err + } + data := []byte(dataStr) + + secret := &v1.Secret{} + if err := json.Unmarshal(data, secret); err != nil { + return err + } + + _, err = c.CoreV1().Secrets(namespace).Create(secret) + + return err +} + +func (c *Client) createProviderDependentMinioDeployment(namespace, artifactRepositoryProvider string) error { + replacements := map[string]string{ + "$(applicationDefaultNamespace)": namespace, + } + + //// AWS S3 doesn't require a specific artifactRepositoryProvider + if artifactRepositoryProvider == "s3" { + return nil + } + + dataStr, err := replaceVariables(filepath.Join("manifest", artifactRepositoryProvider, "deployment.json"), replacements) + if err != nil { + return err + } + data := []byte(dataStr) + + deployment := &vapps.Deployment{} + if err := json.Unmarshal(data, deployment); err != nil { + return err + } + + _, err = c.AppsV1().Deployments(namespace).Create(deployment) + + return err +} + +func (c *Client) createProviderDependentMinioService(namespace, artifactRepositoryProvider string) error { + replacements := map[string]string{ + "$(applicationDefaultNamespace)": namespace, + } + + // AWS S3 doesn't require a specific artifactRepositoryProvider + if artifactRepositoryProvider == "s3" { + return nil + } + + dataStr, err := replaceVariables(filepath.Join("manifest", artifactRepositoryProvider, "service.json"), replacements) + if err != nil { + return err + } + data := []byte(dataStr) + + service := &v1.Service{} + if err := json.Unmarshal(data, service); err != nil { + return err + } + + _, err = c.CoreV1().Services(namespace).Create(service) + + return err +} + +func (c *Client) createNamespaceClusterRoleBinding(namespace string) error { + replacements := map[string]string{ + "$(applicationDefaultNamespace)": namespace, + } + + dataStr, err := replaceVariables(filepath.Join("manifest", "clusterrolebinding-onepanel-namespaces-defaultnamespace.json"), replacements) + if err != nil { + return err + } + data := []byte(dataStr) + + resource := &v1rbac.ClusterRoleBinding{} + if err := json.Unmarshal(data, resource); err != nil { + return err + } + + resource.Name += "-" + namespace + + _, err = c.RbacV1().ClusterRoleBindings().Create(resource) + + return err +} + +func (c *Client) createNamespaceRoleBinding(namespace string) error { + replacements := map[string]string{ + "$(applicationDefaultNamespace)": namespace, + } + + dataStr, err := replaceVariables(filepath.Join("manifest", "rolebinding-onepanel-defaultnamespace.json"), replacements) + if err != nil { + return err + } + data := []byte(dataStr) + + resource := &v1rbac.RoleBinding{} + if err := json.Unmarshal(data, resource); err != nil { + return err + } + + _, err = c.RbacV1().RoleBindings(namespace).Create(resource) + + return err +} + +func (c *Client) createNamespaceServiceAccount(namespace string) error { + replacements := map[string]string{ + "$(applicationDefaultNamespace)": namespace, + } + + dataStr, err := replaceVariables(filepath.Join("manifest", "service-account.json"), replacements) + if err != nil { + return err + } + data := []byte(dataStr) + + resource := &v1.ServiceAccount{} + if err := json.Unmarshal(data, resource); err != nil { + return err + } + + _, err = c.CoreV1().ServiceAccounts(namespace).Create(resource) + + return err +} + +func (c *Client) createNamespaceConfigMap(sourceNamespace, namespace string) error { + sourceConfigMap, err := c.CoreV1().ConfigMaps(sourceNamespace).Get("onepanel", metav1.GetOptions{}) + if err != nil { + return err + } + + data := sourceConfigMap.Data["artifactRepository"] + sourceKey := "minio-gateway." + sourceNamespace + ".svc.cluster.local:9000" + replaceKey := "minio-gateway." + namespace + ".svc.cluster.local:9000" + data = strings.ReplaceAll(data, sourceKey, replaceKey) + sourceConfigMap.Data["artifactRepository"] = data + + configMap := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "onepanel", + Namespace: namespace, + }, + Data: sourceConfigMap.Data, + } + + configMap.Namespace = namespace + + _, err = c.CoreV1().ConfigMaps(namespace).Create(configMap) + + return err +} + +func (c *Client) createNamespaceModelClusterRoleBinding(namespace string) error { + replacements := map[string]string{ + "$(applicationDefaultNamespace)": namespace, + } + + dataStr, err := replaceVariables(filepath.Join("manifest", "clusterrolebinding-models.json"), replacements) + if err != nil { + return err + } + data := []byte(dataStr) + + resource := &v1rbac.ClusterRoleBinding{} + if err := json.Unmarshal(data, resource); err != nil { + return err + } + + _, err = c.RbacV1().ClusterRoleBindings().Create(resource) + + return err +} + +func (c *Client) createNamespaceTemplates(namespace string) error { + wd, err := os.Getwd() + if err != nil { + return err + } + + workflowDir := filepath.Join(wd, "db", "yaml") + + filepaths := make([]string, 0) + + err = filepath.Walk(workflowDir, + func(path string, info os.FileInfo, err error) error { + if !info.IsDir() { + filepaths = append(filepaths, path) + } + + return nil + }, + ) + if err != nil { + return err + } + + for _, filename := range filepaths { + manifest, err := data.ManifestFileFromFile(filename) + if err != nil { + return err + } + + if manifest.Metadata.Kind == "Workflow" { + if manifest.Metadata.Action == "create" { + if err := c.createWorkflowTemplateFromGenericManifest(namespace, manifest); err != nil { + return err + } + } else { + if err := c.updateWorkflowTemplateManifest(namespace, manifest); err != nil { + return err + } + } + } else if manifest.Metadata.Kind == "Workspace" { + if manifest.Metadata.Action == "create" { + if err := c.createWorkspaceTemplateFromGenericManifest(namespace, manifest); err != nil { + return err + } + } else { + if err := c.updateWorkspaceTemplateManifest(namespace, manifest); err != nil { + return err + } + } + } else { + return fmt.Errorf("unknown manifest type for file %v", filename) + } + } + + return nil }