From f1c0f0d31eb3b00447105494a79930005f303e30 Mon Sep 17 00:00:00 2001 From: Andrey Melnikov Date: Tue, 26 Oct 2021 15:26:23 -0700 Subject: [PATCH 1/5] feat: stub out create namespace and add supporting methods from enterprise version --- Dockerfile | 1 + api/api.swagger.json | 5 ++++- api/gen/namespace.pb.go | 16 +++++++++++--- api/proto/namespace.proto | 1 + manifest/.gitignore | 0 pkg/config_types.go | 7 ++++++ pkg/istio.go | 42 +++++++++++++++++++++++++++++++++++ pkg/namespace.go | 45 +++++++++++++++++++------------------- server/auth_server.go | 20 ++++++++++++++++- server/namespace_server.go | 7 ++++-- 10 files changed, 114 insertions(+), 30 deletions(-) create mode 100644 manifest/.gitignore create mode 100644 pkg/istio.go diff --git a/Dockerfile b/Dockerfile index cdb8db9..4db1f1b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,7 @@ FROM golang:1.15.5 COPY --from=builder /go/bin/core . COPY --from=builder /go/src/db ./db COPY --from=builder /go/bin/goose . +COPY --from=builder /go/src/manifest ./manifest EXPOSE 8888 EXPOSE 8887 diff --git a/api/api.swagger.json b/api/api.swagger.json index b4e19b5..8e1cfe8 100644 --- a/api/api.swagger.json +++ b/api/api.swagger.json @@ -3,7 +3,7 @@ "info": { "title": "Onepanel", "description": "Onepanel API", - "version": "1.0.0", + "version": "1.0.2", "contact": { "name": "Onepanel project", "url": "https://github.com/onepanelio/core" @@ -4184,6 +4184,9 @@ "properties": { "name": { "type": "string" + }, + "sourceName": { + "type": "string" } } }, diff --git a/api/gen/namespace.pb.go b/api/gen/namespace.pb.go index 0ad6e6f..c74441f 100644 --- a/api/gen/namespace.pb.go +++ b/api/gen/namespace.pb.go @@ -220,7 +220,8 @@ type Namespace struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + SourceName string `protobuf:"bytes,2,opt,name=sourceName,proto3" json:"sourceName,omitempty"` } func (x *Namespace) Reset() { @@ -262,6 +263,13 @@ func (x *Namespace) GetName() string { return "" } +func (x *Namespace) GetSourceName() string { + if x != nil { + return x.SourceName + } + return "" +} + var File_namespace_proto protoreflect.FileDescriptor var file_namespace_proto_rawDesc = []byte{ @@ -289,9 +297,11 @@ var file_namespace_proto_rawDesc = []byte{ 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x09, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x1f, 0x0a, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x3f, 0x0a, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x32, 0xec, 0x01, 0x0a, 0x10, 0x4e, 0x61, 0x6d, 0x65, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x32, 0xec, 0x01, 0x0a, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x6b, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, diff --git a/api/proto/namespace.proto b/api/proto/namespace.proto index f766f95..be52440 100644 --- a/api/proto/namespace.proto +++ b/api/proto/namespace.proto @@ -40,4 +40,5 @@ message CreateNamespaceRequest { message Namespace { string name = 1; + string sourceName = 2; } \ No newline at end of file diff --git a/manifest/.gitignore b/manifest/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/pkg/config_types.go b/pkg/config_types.go index 536817b..7251043 100644 --- a/pkg/config_types.go +++ b/pkg/config_types.go @@ -174,6 +174,11 @@ func (s SystemConfig) DatabaseDriverName() *string { return s.GetValue("databaseDriverName") } +// Provider gets the ONEPANEL_PROVIDER value, or nil. +func (s SystemConfig) Provider() *string { + return s.GetValue("ONEPANEL_PROVIDER") +} + // DatabaseConnection returns system config information to connect to a database func (s SystemConfig) DatabaseConnection() (driverName, dataSourceName string) { dataSourceName = fmt.Sprintf("host=%v user=%v password=%v dbname=%v sslmode=disable", @@ -243,6 +248,7 @@ func (s SystemConfig) HMACKey() []byte { // by the CLI. CLI will marshal this struct into the correct // YAML structure for k8s configmap / secret. type ArtifactRepositoryS3Provider struct { + Source string KeyFormat string `yaml:"keyFormat"` Bucket string Endpoint string @@ -260,6 +266,7 @@ type ArtifactRepositoryS3Provider struct { // by the CLI. CLI will marshal this struct into the correct // YAML structure for k8s configmap / secret. type ArtifactRepositoryGCSProvider struct { + Source string KeyFormat string `yaml:"keyFormat"` Bucket string Endpoint string diff --git a/pkg/istio.go b/pkg/istio.go new file mode 100644 index 0000000..0773fde --- /dev/null +++ b/pkg/istio.go @@ -0,0 +1,42 @@ +package v1 + +import ( + "fmt" + "github.com/onepanelio/core/pkg/util" + "google.golang.org/grpc/codes" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "strings" +) + +const istioVirtualServiceResource = "VirtualServices" + +func istioModelRestClient() (*rest.RESTClient, error) { + config := *NewConfig() + config.GroupVersion = &schema.GroupVersion{Group: "networking.istio.io", Version: "v1alpha3"} + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + return rest.RESTClientFor(&config) +} + +func (c *Client) CreateVirtualService(namespace string, data interface{}) error { + restClient, err := istioModelRestClient() + if err != nil { + return err + } + + err = restClient.Post(). + Namespace(namespace). + Resource(istioVirtualServiceResource). + Body(data). + Do(). + Error() + + if err != nil && strings.Contains(err.Error(), "already exists") { + return util.NewUserError(codes.AlreadyExists, fmt.Sprintf("VirtualService already exists")) + } + + return err +} diff --git a/pkg/namespace.go b/pkg/namespace.go index 38f731a..46a2921 100644 --- a/pkg/namespace.go +++ b/pkg/namespace.go @@ -2,13 +2,29 @@ package v1 import ( "fmt" - v1 "k8s.io/api/core/v1" - + "github.com/onepanelio/core/pkg/util" + "google.golang.org/grpc/codes" + "io/ioutil" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "strings" ) var onepanelEnabledLabelKey = "onepanel.io/enabled" +func replaceVariables(filepath string, replacements map[string]string) (string, error) { + data, err := ioutil.ReadFile(filepath) + if err != nil { + return "", err + } + + dataStr := string(data) + for key, value := range replacements { + dataStr = strings.ReplaceAll(dataStr, key, value) + } + + return dataStr, nil +} + func (c *Client) ListOnepanelEnabledNamespaces() (namespaces []*Namespace, err error) { namespaceList, err := c.CoreV1().Namespaces().List(metav1.ListOptions{ LabelSelector: fmt.Sprintf("%s=%s", onepanelEnabledLabelKey, "true"), @@ -42,6 +58,7 @@ func (c *Client) GetNamespace(name string) (namespace *Namespace, err error) { return } +// ListNamespaces lists all of the onepanel enabled namespaces func (c *Client) ListNamespaces() (namespaces []*Namespace, err error) { namespaceList, err := c.CoreV1().Namespaces().List(metav1.ListOptions{ LabelSelector: fmt.Sprintf("%s=%s", onepanelEnabledLabelKey, "true"), @@ -60,25 +77,7 @@ func (c *Client) ListNamespaces() (namespaces []*Namespace, err error) { return } -func (c *Client) CreateNamespace(name string) (namespace *Namespace, err error) { - createNamespace := &v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: map[string]string{ - "istio-injection": "enabled", - onepanelEnabledLabelKey: "true", - }, - }, - } - - k8Namespace, err := c.CoreV1().Namespaces().Create(createNamespace) - if err != nil { - return - } - - namespace = &Namespace{ - Name: k8Namespace.Name, - } - - 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") } diff --git a/server/auth_server.go b/server/auth_server.go index 354fa1f..d78129f 100644 --- a/server/auth_server.go +++ b/server/auth_server.go @@ -10,6 +10,7 @@ import ( "github.com/pkg/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // AuthServer contains logic for checking Authorization of resources in the system @@ -73,6 +74,23 @@ func (a *AuthServer) GetAccessToken(ctx context.Context, req *api.GetAccessToken return nil, fmt.Errorf("domain is not set") } + // This is for backwards compatibility + // Originally, when you logged in as the admin, you would get the defaultNamespace as the + // namespace. + if req.Username == "admin" { + nsList, err := client.CoreV1().Namespaces().List(metav1.ListOptions{ + LabelSelector: "onepanel.io/defaultNamespace=true", + }) + + if err != nil { + return nil, err + } + + if len(nsList.Items) == 1 { + req.Username = nsList.Items[0].Name + } + } + res = &api.GetAccessTokenResponse{ Domain: *domain, AccessToken: client.Token, @@ -123,7 +141,7 @@ func (a *AuthServer) isValidToken(err error, client *v1.Client) error { return err } if len(namespaces) == 0 { - return errors.New("No namespaces for onepanel setup.") + return errors.New("no namespaces for onepanel setup.") } namespace := namespaces[0] diff --git a/server/namespace_server.go b/server/namespace_server.go index 9cd1d1d..d654a2a 100644 --- a/server/namespace_server.go +++ b/server/namespace_server.go @@ -28,6 +28,7 @@ func apiNamespace(ns *v1.Namespace) (namespace *api.Namespace) { return } +// ListNamespaces returns a list of all namespaces available in the system func (s *NamespaceServer) ListNamespaces(ctx context.Context, req *api.ListNamespacesRequest) (*api.ListNamespacesResponse, error) { client := getClient(ctx) allowed, err := auth.IsAuthorized(client, "", "list", "", "namespaces", "") @@ -75,6 +76,7 @@ func (s *NamespaceServer) ListNamespaces(ctx context.Context, req *api.ListNames }, nil } +// CreateNamespace creates a new namespace in the system func (s *NamespaceServer) CreateNamespace(ctx context.Context, createNamespace *api.CreateNamespaceRequest) (*api.Namespace, error) { client := getClient(ctx) allowed, err := auth.IsAuthorized(client, "", "create", "", "namespaces", "") @@ -82,12 +84,13 @@ func (s *NamespaceServer) CreateNamespace(ctx context.Context, createNamespace * return nil, err } - namespace, err := client.CreateNamespace(createNamespace.Namespace.Name) + namespace, err := client.CreateNamespace(createNamespace.Namespace.SourceName, createNamespace.Namespace.Name) if err != nil { return nil, err } return &api.Namespace{ - Name: namespace.Name, + Name: namespace.Name, + SourceName: createNamespace.Namespace.SourceName, }, nil } From d934163fc896b37e605db00f6d261efe70fa3021 Mon Sep 17 00:00:00 2001 From: Andrey Melnikov Date: Tue, 26 Oct 2021 15:37:21 -0700 Subject: [PATCH 2/5] fix: code formatting and docs --- pkg/istio.go | 1 + server/auth_server.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/istio.go b/pkg/istio.go index 0773fde..4530ab3 100644 --- a/pkg/istio.go +++ b/pkg/istio.go @@ -21,6 +21,7 @@ func istioModelRestClient() (*rest.RESTClient, error) { return rest.RESTClientFor(&config) } +// CreateVirtualService creates an istio virtual service func (c *Client) CreateVirtualService(namespace string, data interface{}) error { restClient, err := istioModelRestClient() if err != nil { diff --git a/server/auth_server.go b/server/auth_server.go index d78129f..66bb454 100644 --- a/server/auth_server.go +++ b/server/auth_server.go @@ -141,7 +141,7 @@ func (a *AuthServer) isValidToken(err error, client *v1.Client) error { return err } if len(namespaces) == 0 { - return errors.New("no namespaces for onepanel setup.") + return errors.New("no namespaces for onepanel setup") } namespace := namespaces[0] From 62896b2f52d139b30f9f479c79f75de2c81e972e Mon Sep 17 00:00:00 2001 From: Andrey Melnikov Date: Tue, 26 Oct 2021 15:47:13 -0700 Subject: [PATCH 3/5] fix: Deny create namespace permission in community edition --- server/auth/auth.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/auth/auth.go b/server/auth/auth.go index 9ca33ac..f29b871 100644 --- a/server/auth/auth.go +++ b/server/auth/auth.go @@ -105,6 +105,10 @@ func getClient(ctx context.Context, kubeConfig *v1.Config, db *v1.DB, sysConfig } func IsAuthorized(c *v1.Client, namespace, verb, group, resource, name string) (allowed bool, err error) { + if resource == "namespaces" && verb == "create" { + return false, status.Error(codes.PermissionDenied, "creating namespaces is not supported in the community edition") + } + review, err := c.AuthorizationV1().SelfSubjectAccessReviews().Create(&authorizationv1.SelfSubjectAccessReview{ Spec: authorizationv1.SelfSubjectAccessReviewSpec{ ResourceAttributes: &authorizationv1.ResourceAttributes{ From 6407c2a7b4c718b8982c7891a6f6a645e8549d35 Mon Sep 17 00:00:00 2001 From: Andrey Melnikov Date: Wed, 27 Oct 2021 09:24:28 -0700 Subject: [PATCH 4/5] 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 } From 493ca51682f2d982938baf22a64e0ab596f66e2a Mon Sep 17 00:00:00 2001 From: Andrey Melnikov Date: Wed, 27 Oct 2021 09:31:07 -0700 Subject: [PATCH 5/5] fix: docs for CreateNamespace --- pkg/namespace.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/namespace.go b/pkg/namespace.go index 099b11b..252181d 100644 --- a/pkg/namespace.go +++ b/pkg/namespace.go @@ -86,6 +86,7 @@ func (c *Client) ListNamespaces() (namespaces []*Namespace, err error) { return } +// CreateNamespace creates a namespace named {{ name }} assuming the {{ sourceNamespace }} created it func (c *Client) CreateNamespace(sourceNamespace, name string) (namespace *Namespace, err error) { newNamespace := name domain := *c.systemConfig.Domain()