diff --git a/server/data/store-apps/app_list.yaml b/server/data/store-apps/app_list.yaml index 40f716ff..6fa26d77 100644 --- a/server/data/store-apps/app_list.yaml +++ b/server/data/store-apps/app_list.yaml @@ -1,7 +1,8 @@ enabledApps: - - argo-cd - - crossplane - - testkube + - argo-cd-v1.0.0 + - crossplane-v1.0.0 + - testkube-v1.0.0 + - testkube-v1.0.1 - tekton disabledApps: diff --git a/server/data/store-apps/conf/argo-cd.yaml b/server/data/store-apps/conf/argo-cd-v1.0.0.yaml similarity index 100% rename from server/data/store-apps/conf/argo-cd.yaml rename to server/data/store-apps/conf/argo-cd-v1.0.0.yaml diff --git a/server/data/store-apps/conf/crossplane.yaml b/server/data/store-apps/conf/crossplane-v1.0.0.yaml similarity index 100% rename from server/data/store-apps/conf/crossplane.yaml rename to server/data/store-apps/conf/crossplane-v1.0.0.yaml diff --git a/server/data/store-apps/conf/testkube.yaml b/server/data/store-apps/conf/testkube-v1.0.0.yaml similarity index 100% rename from server/data/store-apps/conf/testkube.yaml rename to server/data/store-apps/conf/testkube-v1.0.0.yaml diff --git a/server/data/store-apps/conf/testkube-v1.0.1.yaml b/server/data/store-apps/conf/testkube-v1.0.1.yaml new file mode 100644 index 00000000..bfa0aa45 --- /dev/null +++ b/server/data/store-apps/conf/testkube-v1.0.1.yaml @@ -0,0 +1,12 @@ +Name: "testkube" +ChartName: "tools/testkube" +Category: "Testing framework" +Description: "" +RepoName: "tools" +RepoURL: "https://kube-tarian.github.io/helmrepo-supporting-tools" +Namespace: "testkube" +ReleaseName: "testkube" +Version: "1.0.1" +CreateNamespace: true +OverrideValues: + DomainName: "{{.DomainName}}" \ No newline at end of file diff --git a/server/pkg/api/add_store_apps.go b/server/pkg/api/add_store_apps.go new file mode 100644 index 00000000..cec31121 --- /dev/null +++ b/server/pkg/api/add_store_apps.go @@ -0,0 +1,56 @@ +package api + +import ( + "context" + "encoding/hex" + + "github.com/kube-tarian/kad/server/pkg/pb/serverpb" + "github.com/kube-tarian/kad/server/pkg/types" +) + +func (s *Server) AddStoreApp(ctx context.Context, request *serverpb.AddStoreAppRequest) ( + *serverpb.AddStoreAppResponse, error) { + _, err := validateRequest(ctx, request.AppConfig.AppName, request.AppConfig.Version) + if err != nil { + s.log.Infof("request validation failed", err) + return &serverpb.AddStoreAppResponse{ + Status: serverpb.StatusCode_INVALID_ARGUMENT, + StatusMessage: "request validation failed", + }, nil + } + s.log.Infof("Add store app [%s:%s] request recieved", request.AppConfig.AppName, request.AppConfig.Version) + + config := &types.StoreAppConfig{ + ReleaseName: request.AppConfig.ReleaseName, + AppName: request.AppConfig.AppName, + Version: request.AppConfig.Version, + Category: request.AppConfig.Category, + Description: request.AppConfig.Description, + ChartName: request.AppConfig.ChartName, + RepoName: request.AppConfig.RepoName, + RepoURL: request.AppConfig.RepoURL, + Namespace: request.AppConfig.Namespace, + CreateNamespace: request.AppConfig.CreateNamespace, + PrivilegedNamespace: request.AppConfig.PrivilegedNamespace, + Icon: hex.EncodeToString(request.AppConfig.Icon), + LaunchURL: request.AppConfig.LaunchURL, + LaunchUIDescription: request.AppConfig.LaunchUIDescription, + OverrideValues: encodeBase64BytesToString(request.AppValues.OverrideValues), + LaunchUIValues: encodeBase64BytesToString(request.AppValues.LaunchUIValues), + TemplateValues: encodeBase64BytesToString(request.AppValues.TemplateValues), + } + + if err := s.serverStore.AddOrUpdateStoreApp(config); err != nil { + s.log.Errorf("failed to add app config to store, %v", err) + return &serverpb.AddStoreAppResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed add app config to store", + }, nil + } + + s.log.Infof("Add store app [%s:%s] request successful", request.AppConfig.AppName, request.AppConfig.Version) + return &serverpb.AddStoreAppResponse{ + Status: serverpb.StatusCode_OK, + StatusMessage: "app config is sucessfuly added to store", + }, nil +} diff --git a/server/pkg/api/app_config_mapping.go b/server/pkg/api/app_config_mapping.go new file mode 100644 index 00000000..a85711f4 --- /dev/null +++ b/server/pkg/api/app_config_mapping.go @@ -0,0 +1,44 @@ +package api + +import ( + "github.com/kube-tarian/kad/server/pkg/pb/agentpb" + "github.com/kube-tarian/kad/server/pkg/pb/serverpb" +) + +func mapAgentAppsToServerResp(appDataList []*agentpb.AppData) []*serverpb.ClusterAppConfig { + clusterAppConfigs := make([]*serverpb.ClusterAppConfig, len(appDataList)) + for index, appConfig := range appDataList { + var clusterAppConfig serverpb.ClusterAppConfig + clusterAppConfig.AppName = appConfig.Config.AppName + clusterAppConfig.Version = appConfig.Config.Version + clusterAppConfig.Category = appConfig.Config.Category + clusterAppConfig.Description = appConfig.Config.Description + clusterAppConfig.ChartName = appConfig.Config.ChartName + clusterAppConfig.RepoName = appConfig.Config.RepoName + clusterAppConfig.RepoURL = appConfig.Config.RepoURL + clusterAppConfig.Namespace = appConfig.Config.Namespace + clusterAppConfig.CreateNamespace = appConfig.Config.CreateNamespace + clusterAppConfig.PrivilegedNamespace = appConfig.Config.PrivilegedNamespace + clusterAppConfig.Icon = appConfig.Config.Icon + clusterAppConfig.LaunchURL = appConfig.Config.LaunchURL + clusterAppConfig.InstallStatus = appConfig.Config.InstallStatus + clusterAppConfig.RuntimeStatus = "" + clusterAppConfig.DefualtApp = appConfig.Config.DefualtApp + clusterAppConfigs[index] = &clusterAppConfig + } + return clusterAppConfigs +} + +func mapAgentAppLauncesToServerResp(appLaunchCfgs []*agentpb.AppLaunchConfig) []*serverpb.AppLaunchConfig { + svrAppLaunchCfg := make([]*serverpb.AppLaunchConfig, len(appLaunchCfgs)) + for index, cfg := range appLaunchCfgs { + var launchCfg serverpb.AppLaunchConfig + launchCfg.ReleaseName = cfg.ReleaseName + launchCfg.Category = cfg.Category + launchCfg.LaunchUIDescription = cfg.Description + launchCfg.Icon = cfg.Icon + launchCfg.LaunchURL = cfg.LaunchURL + svrAppLaunchCfg[index] = &launchCfg + } + return svrAppLaunchCfg +} diff --git a/server/pkg/api/app_values_utils.go b/server/pkg/api/app_values_utils.go new file mode 100644 index 00000000..a3788131 --- /dev/null +++ b/server/pkg/api/app_values_utils.go @@ -0,0 +1,62 @@ +package api + +import ( + "bytes" + "context" + "fmt" + "text/template" + + "github.com/kube-tarian/kad/server/pkg/pb/agentpb" + "github.com/pkg/errors" + "gopkg.in/yaml.v2" +) + +func (s *Server) replaceGlobalValues(orgId, clusterID string, overridedValues []byte) ([]byte, error) { + agent, err := s.agentHandeler.GetAgent(orgId, clusterID) + if err != nil { + return nil, errors.WithMessagef(err, "failed to initialize agent for cluster %s", clusterID) + } + resp, err := agent.GetClient().GetClusterGlobalValues(context.TODO(), &agentpb.GetClusterGlobalValuesRequest{}) + if err != nil { + return nil, err + } + if resp.Status != agentpb.StatusCode_OK { + return nil, fmt.Errorf("failed to get global values for cluster %s", clusterID) + } + + var globalValues map[string]interface{} + err = yaml.Unmarshal(resp.GlobalValues, &globalValues) + if err != nil { + return nil, errors.WithMessagef(err, "failed to unmarshal cluster values") + } + + var overrideValues map[string]interface{} + err = yaml.Unmarshal(overridedValues, &overrideValues) + if err != nil { + return nil, errors.WithMessagef(err, "failed to unmarshal override values") + } + + return replaceOverrideGlobalValues(overrideValues, globalValues) +} + +func replaceOverrideGlobalValues(overrideValues map[string]interface{}, + globlaValues map[string]interface{}) (transformedData []byte, err error) { + yamlData, err := yaml.Marshal(overrideValues) + if err != nil { + return + } + + tmpl, err := template.New("templateVal").Parse(string(yamlData)) + if err != nil { + return + } + + var buf bytes.Buffer + err = tmpl.Execute(&buf, globlaValues) + if err != nil { + return + } + + transformedData = buf.Bytes() + return +} diff --git a/server/pkg/api/cluster_apps.go b/server/pkg/api/cluster_apps.go deleted file mode 100644 index da01de40..00000000 --- a/server/pkg/api/cluster_apps.go +++ /dev/null @@ -1,156 +0,0 @@ -package api - -import ( - "context" - "fmt" - "time" - - "github.com/kube-tarian/kad/server/pkg/pb/agentpb" - "github.com/kube-tarian/kad/server/pkg/pb/serverpb" - "github.com/pkg/errors" -) - -func mapAgentAppsToServerResp(appDataList []*agentpb.AppData) []*serverpb.ClusterAppConfig { - clusterAppConfigs := make([]*serverpb.ClusterAppConfig, len(appDataList)) - for index, appConfig := range appDataList { - var clusterAppConfig serverpb.ClusterAppConfig - clusterAppConfig.AppName = appConfig.Config.AppName - clusterAppConfig.Version = appConfig.Config.Version - clusterAppConfig.Category = appConfig.Config.Category - clusterAppConfig.Description = appConfig.Config.Description - clusterAppConfig.ChartName = appConfig.Config.ChartName - clusterAppConfig.RepoName = appConfig.Config.RepoName - clusterAppConfig.RepoURL = appConfig.Config.RepoURL - clusterAppConfig.Namespace = appConfig.Config.Namespace - clusterAppConfig.CreateNamespace = appConfig.Config.CreateNamespace - clusterAppConfig.PrivilegedNamespace = appConfig.Config.PrivilegedNamespace - clusterAppConfig.Icon = appConfig.Config.Icon - clusterAppConfig.LaunchURL = appConfig.Config.LaunchURL - clusterAppConfig.InstallStatus = appConfig.Config.InstallStatus - clusterAppConfig.RuntimeStatus = "" - - clusterAppConfigs[index] = &clusterAppConfig - } - - return clusterAppConfigs - -} - -func mapAgentAppLauncesToServerResp(appLaunchCfgs []*agentpb.AppLaunchConfig) []*serverpb.AppLaunchConfig { - svrAppLaunchCfg := make([]*serverpb.AppLaunchConfig, len(appLaunchCfgs)) - for index, cfg := range appLaunchCfgs { - var launchCfg serverpb.AppLaunchConfig - launchCfg.ReleaseName = cfg.ReleaseName - launchCfg.Category = cfg.Category - launchCfg.LaunchUIDescription = cfg.Description - launchCfg.Icon = cfg.Icon - launchCfg.LaunchURL = cfg.LaunchURL - svrAppLaunchCfg[index] = &launchCfg - } - return svrAppLaunchCfg -} - -func (s *Server) GetClusterApps(ctx context.Context, request *serverpb.GetClusterAppsRequest) ( - *serverpb.GetClusterAppsResponse, error) { - metadataMap := metadataContextToMap(ctx) - orgId := metadataMap[organizationIDAttribute] - if len(orgId) == 0 || request.ClusterID == "" { - s.log.Error("organizationID/ClusterID is missing in the request") - return &serverpb.GetClusterAppsResponse{ - Status: serverpb.StatusCode_INVALID_ARGUMENT, - StatusMessage: "organizationID is missing", - }, nil - } - s.log.Infof("[org: %s] GetClusterApps request recieved for cluster %s", orgId, request.ClusterID) - - a, err := s.agentHandeler.GetAgent(orgId, request.ClusterID) - if err != nil { - s.log.Error("failed to connect to agent", err) - return &serverpb.GetClusterAppsResponse{Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to connect to agent"}, nil - } - - resp, err := a.GetClient().GetClusterApps(ctx, &agentpb.GetClusterAppsRequest{}) - if err != nil { - s.log.Error("failed to get cluster application from agent", err) - return &serverpb.GetClusterAppsResponse{Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to get cluster application from agent"}, nil - } - - s.log.Infof("[org: %s] Fetched %d installed apps from the cluster %s", orgId, len(resp.AppData), request.ClusterID) - return &serverpb.GetClusterAppsResponse{Status: serverpb.StatusCode_OK, - StatusMessage: "successfully fetched the data from agent", - AppConfigs: mapAgentAppsToServerResp(resp.AppData)}, nil -} - -func (s *Server) GetClusterAppLaunchConfigs(ctx context.Context, request *serverpb.GetClusterAppLaunchConfigsRequest) ( - *serverpb.GetClusterAppLaunchConfigsResponse, error) { - metadataMap := metadataContextToMap(ctx) - orgId := metadataMap[organizationIDAttribute] - if len(orgId) == 0 || request.ClusterID == "" { - s.log.Error("organizationID/ClusterID is missing in the request") - return &serverpb.GetClusterAppLaunchConfigsResponse{ - Status: serverpb.StatusCode_INVALID_ARGUMENT, - StatusMessage: "organizationID is missing", - }, nil - } - - s.log.Infof("[org: %s] GetClusterAppLaunchConfigs request recieved for cluster %s", orgId, request.ClusterID) - - resp, err := s.GetClusterAppLaunchesFromCacheOrAgent(ctx, orgId, request.ClusterID) - if err != nil { - s.log.Error("failed to get cluster application launches from agent", err) - return &serverpb.GetClusterAppLaunchConfigsResponse{Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to get cluster application launches from agent"}, err - } - - s.log.Infof("[org: %s] Fetched %d app launch UIs from the cluster %s", orgId, len(resp.LaunchConfigList), request.ClusterID) - return &serverpb.GetClusterAppLaunchConfigsResponse{Status: serverpb.StatusCode_OK, - StatusMessage: "successfully fetched the data from agent", - AppLaunchConfig: mapAgentAppLauncesToServerResp(resp.LaunchConfigList)}, nil -} - -func (s *Server) GetClusterApp(ctx context.Context, request *serverpb.GetClusterAppRequest) ( - *serverpb.GetClusterAppResponse, error) { - return &serverpb.GetClusterAppResponse{}, nil -} - -func (s *Server) configureSSOForClusterApps(ctx context.Context, orgId, clusterID string) error { - agentClient, err := s.agentHandeler.GetAgent(orgId, clusterID) - if err != nil { - return errors.WithMessagef(err, "failed to get agent for cluster %s", clusterID) - } - - resp, err := agentClient.GetClient().GetClusterAppLaunches(ctx, &agentpb.GetClusterAppLaunchesRequest{}) - if err != nil || resp == nil || resp.Status != agentpb.StatusCode_OK { - return fmt.Errorf("failed to get cluster app launches from cluster %s, err: %v", clusterID, resp) - } - - if err := s.serverStore.InsertClusterAppLaunches(orgId, clusterID, resp.LaunchConfigList); err != nil { - return fmt.Errorf("failed to store cluster app launches on server db %s, err: %v", clusterID, err) - } - - s.mutex.Lock() - s.orgClusterIDCache[orgId+"-"+clusterID] = time.Now().Add(delayTimeinMin * time.Minute).Unix() - s.mutex.Unlock() - - for _, app := range resp.LaunchConfigList { - appName := fmt.Sprintf("%s-%s", clusterID, app.ReleaseName) - clientID, clientSecret, err := s.iam.RegisterAppClientSecrets(ctx, appName, app.LaunchURL) - if err != nil { - return errors.WithMessagef(err, "failed to register app %s on cluster %s with IAM", app.ReleaseName, clusterID) - } - - ssoResp, err := agentClient.GetClient().ConfigureAppSSO(ctx, &agentpb.ConfigureAppSSORequest{ - ReleaseName: app.ReleaseName, - ClientId: clientID, - ClientSecret: clientSecret, - OAuthBaseURL: s.cfg.CaptenOAuthURL, - }) - - if err != nil || ssoResp == nil || ssoResp.Status != agentpb.StatusCode_OK { - return fmt.Errorf("failed to configure sso for app %s on cluster %s, err: %v", app.ReleaseName, clusterID, ssoResp) - } - } - return nil -} diff --git a/server/pkg/api/cluster_registration.go b/server/pkg/api/cluster_registration.go deleted file mode 100644 index 74e33ff5..00000000 --- a/server/pkg/api/cluster_registration.go +++ /dev/null @@ -1,339 +0,0 @@ -package api - -import ( - "context" - "encoding/base64" - "time" - - "github.com/gocql/gocql" - "github.com/kube-tarian/kad/server/pkg/agent" - "github.com/kube-tarian/kad/server/pkg/credential" - "github.com/kube-tarian/kad/server/pkg/pb/agentpb" - "github.com/kube-tarian/kad/server/pkg/pb/serverpb" -) - -func (s *Server) NewClusterRegistration(ctx context.Context, request *serverpb.NewClusterRegistrationRequest) ( - *serverpb.NewClusterRegistrationResponse, error) { - metadataMap := metadataContextToMap(ctx) - orgId := metadataMap[organizationIDAttribute] - if len(orgId) == 0 { - s.log.Error("organizationID is missing in the request") - return &serverpb.NewClusterRegistrationResponse{ - Status: serverpb.StatusCode_INVALID_ARGUMENT, - StatusMessage: "organizationID is missing", - }, nil - } - - s.log.Infof("[org: %s] New cluster registration request for cluster %s recieved", orgId, request.ClusterName) - clusterID := gocql.TimeUUID().String() - - caData, caDataErr := s.getBase64DecodedString(request.ClientCAChainData) - clientKey, clientKeyErr := s.getBase64DecodedString(request.ClientKeyData) - clientCrt, clientCrtErr := s.getBase64DecodedString(request.ClientCertData) - if caDataErr != nil || clientKeyErr != nil || clientCrtErr != nil { - return &serverpb.NewClusterRegistrationResponse{ - Status: serverpb.StatusCode_INVALID_ARGUMENT, - StatusMessage: "only base64 encoded certificates are allowed", - }, nil - } - - agentConfig := &agent.Config{ - ClusterName: request.ClusterName, - Address: request.AgentEndpoint, - CaCert: caData, - Key: clientKey, - Cert: clientCrt, - } - if err := s.agentHandeler.AddAgent(clusterID, agentConfig); err != nil { - s.log.Errorf("[org: %s] failed to connect to agent on cluster %s, %v", orgId, request.ClusterName, err) - return &serverpb.NewClusterRegistrationResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to connect to agent", - }, nil - } - - err := credential.PutClusterCerts(ctx, clusterID, - caData, clientKey, clientCrt) - if err != nil { - s.log.Errorf("[org: %s] failed to store cert in vault for cluster %s, %v", orgId, clusterID, err) - return &serverpb.NewClusterRegistrationResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to store cluster credentials", - }, nil - } - - err = s.serverStore.AddCluster(orgId, clusterID, request.ClusterName, request.AgentEndpoint) - if err != nil { - s.log.Errorf("[org: %s] failed to store cluster %s to db, %v", orgId, request.ClusterName, err) - return &serverpb.NewClusterRegistrationResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to store cluster registration", - }, nil - } - - if s.cfg.RegisterLaunchAppsConifg { - if err := s.configureSSOForClusterApps(ctx, orgId, clusterID); err != nil { - s.log.Errorf("[org: %s] %v", orgId, err) - return &serverpb.NewClusterRegistrationResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to configure SSO for cluster apps", - }, nil - } - } - - s.log.Infof("[org: %s] New cluster registration successful for %s/%s cluster", orgId, request.ClusterName, clusterID) - return &serverpb.NewClusterRegistrationResponse{ - Status: serverpb.StatusCode_OK, - StatusMessage: "register cluster success", - ClusterID: clusterID, - }, nil -} - -func (s *Server) UpdateClusterRegistration(ctx context.Context, request *serverpb.UpdateClusterRegistrationRequest) ( - *serverpb.UpdateClusterRegistrationResponse, error) { - metadataMap := metadataContextToMap(ctx) - orgId := metadataMap[organizationIDAttribute] - if len(orgId) == 0 { - s.log.Error("organizationID is missing in the request") - return &serverpb.UpdateClusterRegistrationResponse{ - Status: serverpb.StatusCode_INVALID_ARGUMENT, - StatusMessage: "organizationID is missing", - }, nil - } - - s.log.Infof("[org: %s] Update cluster registration request for cluster %s recieved", orgId, request.ClusterName) - - caData, caDataErr := s.getBase64DecodedString(request.ClientCAChainData) - clientKey, clientKeyErr := s.getBase64DecodedString(request.ClientKeyData) - clientCrt, clientCrtErr := s.getBase64DecodedString(request.ClientCertData) - if caDataErr != nil || clientKeyErr != nil || clientCrtErr != nil { - return &serverpb.UpdateClusterRegistrationResponse{ - Status: serverpb.StatusCode_INVALID_ARGUMENT, - StatusMessage: "only base64 encoded certificates are allowed", - }, nil - } - - agentConfig := &agent.Config{ - ClusterName: request.ClusterName, - Address: request.AgentEndpoint, - CaCert: caData, - Key: clientKey, - Cert: clientCrt, - } - - if err := s.agentHandeler.UpdateAgent(request.ClusterID, agentConfig); err != nil { - s.log.Errorf("[org: %s] failed to connect to agent on cluster %s, %v", orgId, request.ClusterName, err) - return &serverpb.UpdateClusterRegistrationResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to connect to agent", - }, nil - } - - err := credential.PutClusterCerts(ctx, request.ClusterID, - caData, clientKey, clientCrt) - if err != nil { - s.log.Errorf("[org: %s] failed to update cert in vault for cluster %s, %v", orgId, request.ClusterID, err) - return &serverpb.UpdateClusterRegistrationResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed update register cluster", - }, nil - } - - err = s.serverStore.UpdateCluster(orgId, request.ClusterID, request.ClusterName, request.AgentEndpoint) - if err != nil { - s.log.Errorf("[org: %s] failed to update cluster %s in db, %v", orgId, request.ClusterName, err) - return &serverpb.UpdateClusterRegistrationResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed update register cluster", - }, nil - } - - s.log.Infof("[org: %s] Update cluster registration successful for %s/%s cluster", orgId, request.ClusterName, request.ClusterID) - return &serverpb.UpdateClusterRegistrationResponse{ - Status: serverpb.StatusCode_OK, - StatusMessage: "cluster register update success", - }, nil -} - -func (s *Server) DeleteClusterRegistration(ctx context.Context, request *serverpb.DeleteClusterRegistrationRequest) ( - *serverpb.DeleteClusterRegistrationResponse, error) { - metadataMap := metadataContextToMap(ctx) - orgId := metadataMap[organizationIDAttribute] - if len(orgId) == 0 { - s.log.Error("organizationID is missing in the request") - return &serverpb.DeleteClusterRegistrationResponse{ - Status: serverpb.StatusCode_INVALID_ARGUMENT, - StatusMessage: "organizationID is missing", - }, nil - } - - s.log.Infof("[org: %s] Delete cluster registration request for cluster %s recieved", orgId, request.ClusterID) - s.agentHandeler.RemoveAgent(request.ClusterID) - err := credential.DeleteClusterCerts(ctx, request.ClusterID) - if err != nil { - s.log.Errorf("[org: %s] failed to delete cert in vault for cluster %s, %v", orgId, request.ClusterID, err) - return &serverpb.DeleteClusterRegistrationResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed delete register cluster", - }, nil - } - - err = s.serverStore.DeleteFullClusterAppLaunches(orgId, request.ClusterID) - if err != nil { - s.log.Errorf("[org: %s] failed to delete clusterappLaunches %s from db, %v", orgId, request.ClusterID, err) - return &serverpb.DeleteClusterRegistrationResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed delete register cluster", - }, nil - } - - err = s.serverStore.DeleteCluster(orgId, request.ClusterID) - if err != nil { - s.log.Errorf("[org: %s] failed to delete cluster %s from db, %v", orgId, request.ClusterID, err) - return &serverpb.DeleteClusterRegistrationResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed delete register cluster", - }, nil - } - - s.log.Infof("[org: %s] Delete cluster registration request for cluster %s successful", orgId, request.ClusterID) - return &serverpb.DeleteClusterRegistrationResponse{ - Status: serverpb.StatusCode_OK, - StatusMessage: "cluster deletion success", - }, nil -} - -func (s *Server) GetClusters(ctx context.Context, request *serverpb.GetClustersRequest) ( - *serverpb.GetClustersResponse, error) { - metadataMap := metadataContextToMap(ctx) - orgId := metadataMap[organizationIDAttribute] - if len(orgId) == 0 { - s.log.Error("organizationID is missing in the request") - return &serverpb.GetClustersResponse{ - Status: serverpb.StatusCode_INVALID_ARGUMENT, - StatusMessage: "organizationID is missing", - }, nil - } - - s.log.Infof("[org: %s] GetClusters request recieved", orgId) - clusterDetails, err := s.serverStore.GetClusters(orgId) - if err != nil { - s.log.Errorf("[org: %s] failed to get clusters, %v", orgId, err) - return &serverpb.GetClustersResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed get cluster details", - }, err - } - - var data []*serverpb.ClusterInfo - for _, cluster := range clusterDetails { - resp, err := s.GetClusterAppLaunchesFromCacheOrAgent(ctx, orgId, cluster.ClusterID) - if err != nil { - s.log.Errorf("failed to get cluster appLaunches for cluster: %s, %v", cluster.ClusterID, err) - continue - } - - attributes := []*serverpb.ClusterAttribute{} - data = append(data, &serverpb.ClusterInfo{ - ClusterID: cluster.ClusterID, - ClusterName: cluster.ClusterName, - AgentEndpoint: cluster.Endpoint, - Attributes: attributes, - AppLaunchConfigs: mapAgentAppLauncesToServerResp(resp.LaunchConfigList), - }) - } - - s.log.Infof("[org: %s] Found %d clusters", orgId, len(data)) - return &serverpb.GetClustersResponse{ - Status: serverpb.StatusCode_OK, - StatusMessage: "get cluster details success", - Data: data, - }, nil -} - -func (s *Server) GetCluster(ctx context.Context, request *serverpb.GetClusterRequest) ( - *serverpb.GetClusterResponse, error) { - metadataMap := metadataContextToMap(ctx) - orgId := metadataMap[organizationIDAttribute] - if len(orgId) == 0 { - s.log.Error("organizationID is missing in the request") - return &serverpb.GetClusterResponse{ - Status: serverpb.StatusCode_INVALID_ARGUMENT, - StatusMessage: "organizationID is missing", - }, nil - } - - s.log.Infof("[org: %s] GetCluster request recieved for cluster %s", orgId, request.ClusterID) - clusterDetails, err := s.serverStore.GetClusterDetails(orgId, request.ClusterID) - if err != nil { - s.log.Errorf("[org: %s] failed to get cluster %s, %v", orgId, request.ClusterID, err) - return &serverpb.GetClusterResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed get cluster details", - }, err - } - - resp, err := s.GetClusterAppLaunchesFromCacheOrAgent(ctx, orgId, request.ClusterID) - if err != nil || resp == nil || resp.Status != agentpb.StatusCode_OK { - s.log.Error("failed to get cluster application launches from cache/agent: %v", resp) - return &serverpb.GetClusterResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed get cluster app lauches", - }, err - } - - attributes := []*serverpb.ClusterAttribute{} - data := &serverpb.ClusterInfo{ - ClusterID: request.ClusterID, - ClusterName: clusterDetails.ClusterName, - AgentEndpoint: clusterDetails.Endpoint, - Attributes: attributes, - AppLaunchConfigs: mapAgentAppLauncesToServerResp(resp.LaunchConfigList), - } - - s.log.Infof("[org: %s] GetCluster request processed for cluster %s", orgId, request.ClusterID) - return &serverpb.GetClusterResponse{ - Status: serverpb.StatusCode_OK, - StatusMessage: "get cluster details success", - Data: data, - }, nil -} - -func (s *Server) getBase64DecodedString(encodedString string) (string, error) { - decodedByte, err := base64.StdEncoding.DecodeString(encodedString) - if err != nil { - // This will assume the string is not encoded and returns the original string. - s.log.Errorf("Failed to decode the string: %v", err) - return "", err - } - - return string(decodedByte), nil -} - -func (s *Server) GetClusterAppLaunchesFromCacheOrAgent(ctx context.Context, orgId, clusterID string) ( - *agentpb.GetClusterAppLaunchesResponse, error) { - currentTime := time.Now() - lastFetchedTime := time.Unix(s.orgClusterIDCache[orgId+"-"+clusterID], 0) - - if currentTime.After(lastFetchedTime) { - // cache expired re-trigger the cache - agentClient, aErr := s.agentHandeler.GetAgent(orgId, clusterID) - if aErr == nil { - resp, err := agentClient.GetClient().GetClusterAppLaunches(ctx, &agentpb.GetClusterAppLaunchesRequest{}) - if err == nil { - updateErr := s.serverStore.UpdateClusterAppLaunches(orgId, clusterID, resp.LaunchConfigList) - if updateErr == nil { - s.mutex.Lock() - s.orgClusterIDCache[orgId+"-"+clusterID] = currentTime.Add(delayTimeinMin * time.Minute).Unix() - s.mutex.Unlock() - - return resp, err - } - - } - } - } - - // If any failure happens return from cache. - return s.serverStore.GetClusterAppLaunches(orgId, clusterID) -} diff --git a/server/pkg/api/configure_app_sso.go b/server/pkg/api/configure_app_sso.go new file mode 100644 index 00000000..067def40 --- /dev/null +++ b/server/pkg/api/configure_app_sso.go @@ -0,0 +1,50 @@ +package api + +import ( + "context" + "fmt" + "time" + + "github.com/kube-tarian/kad/server/pkg/pb/agentpb" + "github.com/pkg/errors" +) + +func (s *Server) configureSSOForClusterApps(ctx context.Context, orgId, clusterID string) error { + agentClient, err := s.agentHandeler.GetAgent(orgId, clusterID) + if err != nil { + return errors.WithMessagef(err, "failed to get agent for cluster %s", clusterID) + } + + resp, err := agentClient.GetClient().GetClusterAppLaunches(ctx, &agentpb.GetClusterAppLaunchesRequest{}) + if err != nil || resp == nil || resp.Status != agentpb.StatusCode_OK { + return fmt.Errorf("failed to get cluster app launches from cluster %s, err: %v", clusterID, resp) + } + + if err := s.serverStore.InsertClusterAppLaunches(orgId, clusterID, resp.LaunchConfigList); err != nil { + return fmt.Errorf("failed to store cluster app launches on server db %s, err: %v", clusterID, err) + } + + s.mutex.Lock() + s.orgClusterIDCache[orgId+"-"+clusterID] = time.Now().Add(delayTimeinMin * time.Minute).Unix() + s.mutex.Unlock() + + for _, app := range resp.LaunchConfigList { + appName := fmt.Sprintf("%s-%s", clusterID, app.ReleaseName) + clientID, clientSecret, err := s.iam.RegisterAppClientSecrets(ctx, appName, app.LaunchURL) + if err != nil { + return errors.WithMessagef(err, "failed to register app %s on cluster %s with IAM", app.ReleaseName, clusterID) + } + + ssoResp, err := agentClient.GetClient().ConfigureAppSSO(ctx, &agentpb.ConfigureAppSSORequest{ + ReleaseName: app.ReleaseName, + ClientId: clientID, + ClientSecret: clientSecret, + OAuthBaseURL: s.cfg.CaptenOAuthURL, + }) + + if err != nil || ssoResp == nil || ssoResp.Status != agentpb.StatusCode_OK { + return fmt.Errorf("failed to configure sso for app %s on cluster %s, err: %v", app.ReleaseName, clusterID, ssoResp) + } + } + return nil +} diff --git a/server/pkg/api/delete_cluster_registration.go b/server/pkg/api/delete_cluster_registration.go new file mode 100644 index 00000000..ab5c99d5 --- /dev/null +++ b/server/pkg/api/delete_cluster_registration.go @@ -0,0 +1,55 @@ +package api + +import ( + "context" + + "github.com/kube-tarian/kad/server/pkg/credential" + "github.com/kube-tarian/kad/server/pkg/pb/serverpb" +) + +func (s *Server) DeleteClusterRegistration(ctx context.Context, request *serverpb.DeleteClusterRegistrationRequest) ( + *serverpb.DeleteClusterRegistrationResponse, error) { + orgId, err := validateRequest(ctx, request.ClusterID) + if err != nil { + s.log.Infof("request validation failed", err) + return &serverpb.DeleteClusterRegistrationResponse{ + Status: serverpb.StatusCode_INVALID_ARGUMENT, + StatusMessage: "request validation failed", + }, nil + } + + s.log.Infof("Delete cluster registration request for cluster %s recieved, [org: %s]", request.ClusterID, orgId) + s.agentHandeler.RemoveAgent(request.ClusterID) + err = credential.DeleteClusterCerts(ctx, request.ClusterID) + if err != nil { + s.log.Errorf("failed to delete cert in vault for cluster %s, %v", request.ClusterID, err) + return &serverpb.DeleteClusterRegistrationResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed delete register cluster", + }, nil + } + + err = s.serverStore.DeleteFullClusterAppLaunches(orgId, request.ClusterID) + if err != nil { + s.log.Errorf("failed to delete clusterappLaunches %s from db, %v", request.ClusterID, err) + return &serverpb.DeleteClusterRegistrationResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed delete register cluster", + }, nil + } + + err = s.serverStore.DeleteCluster(orgId, request.ClusterID) + if err != nil { + s.log.Errorf("failed to delete cluster %s from db, %v", request.ClusterID, err) + return &serverpb.DeleteClusterRegistrationResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed delete register cluster", + }, nil + } + + s.log.Infof("Delete cluster registration request for cluster %s successful, [org: %s]", request.ClusterID, orgId) + return &serverpb.DeleteClusterRegistrationResponse{ + Status: serverpb.StatusCode_OK, + StatusMessage: "cluster deletion success", + }, nil +} diff --git a/server/pkg/api/delete_store_app.go b/server/pkg/api/delete_store_app.go new file mode 100644 index 00000000..b3a5d651 --- /dev/null +++ b/server/pkg/api/delete_store_app.go @@ -0,0 +1,35 @@ +package api + +import ( + "context" + + "github.com/kube-tarian/kad/server/pkg/pb/serverpb" +) + +func (s *Server) DeleteStoreApp(ctx context.Context, request *serverpb.DeleteStoreAppRequest) ( + *serverpb.DeleteStoreAppResponse, error) { + _, err := validateRequest(ctx, request.AppName, request.Version) + if err != nil { + s.log.Infof("request validation failed", err) + return &serverpb.DeleteStoreAppResponse{ + Status: serverpb.StatusCode_INVALID_ARGUMENT, + StatusMessage: "request validation failed", + }, nil + } + s.log.Infof("Delete store app [%s:%s] request recieved", request.AppName, request.Version) + + if err := s.serverStore.DeleteAppInStore(request.AppName, request.Version); err != nil { + s.log.Errorf("failed to delete app config from store, %v", err) + return &serverpb.DeleteStoreAppResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to delete app config from store", + }, nil + } + + s.log.Infof("Delete store app [%s:%s] request successful", request.AppName, request.Version) + return &serverpb.DeleteStoreAppResponse{ + Status: serverpb.StatusCode_OK, + StatusMessage: "app config is sucessfuly deleted", + }, nil + +} diff --git a/server/pkg/api/deploy_store_app.go b/server/pkg/api/deploy_store_app.go new file mode 100644 index 00000000..0519206a --- /dev/null +++ b/server/pkg/api/deploy_store_app.go @@ -0,0 +1,101 @@ +package api + +import ( + "context" + "encoding/hex" + + "github.com/kube-tarian/kad/server/pkg/pb/agentpb" + "github.com/kube-tarian/kad/server/pkg/pb/serverpb" + "gopkg.in/yaml.v2" +) + +func (s *Server) DeployStoreApp(ctx context.Context, request *serverpb.DeployStoreAppRequest) ( + *serverpb.DeployStoreAppResponse, error) { + orgId, err := validateRequest(ctx, request.ClusterID, request.AppName, request.Version) + if err != nil { + s.log.Infof("request validation failed", err) + return &serverpb.DeployStoreAppResponse{ + Status: serverpb.StatusCode_INVALID_ARGUMENT, + StatusMessage: "request validation failed", + }, nil + } + s.log.Infof("Deploy store app [%s:%s] request for cluster %s recieved, [org: %s]", + request.AppName, request.Version, request.ClusterID, orgId) + + config, err := s.serverStore.GetAppFromStore(request.AppName, request.Version) + if err != nil { + s.log.Errorf("failed to get store app values, %v", err) + return &serverpb.DeployStoreAppResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to find store app values", + }, nil + } + + marshaledOverride, err := yaml.Marshal(config.OverrideValues) + if err != nil { + return &serverpb.DeployStoreAppResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to find store app values", + }, nil + } + + marshaledLaunchUi, err := yaml.Marshal(config.LaunchUIValues) + if err != nil { + return &serverpb.DeployStoreAppResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to find store app values", + }, nil + } + + decodedIconBytes, _ := hex.DecodeString(config.Icon) + req := &agentpb.InstallAppRequest{ + AppConfig: &agentpb.AppConfig{ + AppName: config.Name, + Version: config.Version, + ReleaseName: config.ReleaseName, + Category: config.Category, + Description: config.Description, + ChartName: config.ChartName, + RepoName: config.RepoName, + RepoURL: config.RepoURL, + Namespace: config.Namespace, + CreateNamespace: config.CreateNamespace, + PrivilegedNamespace: config.PrivilegedNamespace, + Icon: decodedIconBytes, + LaunchURL: config.LaunchURL, + LaunchUIDescription: config.LaunchUIDescription, + DefualtApp: false, + }, + AppValues: &agentpb.AppValues{ + OverrideValues: request.OverrideValues, + LaunchUIValues: decodeBase64StringToBytes(string(marshaledLaunchUi)), + TemplateValues: decodeBase64StringToBytes(string(marshaledOverride)), + }, + } + + agent, err := s.agentHandeler.GetAgent(orgId, request.ClusterID) + if err != nil { + s.log.Errorf("failed to initialize agent, %v", err) + return &serverpb.DeployStoreAppResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to deploy the app", + }, nil + } + + _, err = agent.GetClient().InstallApp(ctx, req) + if err != nil { + s.log.Errorf("failed to deploy app, %v", err) + return &serverpb.DeployStoreAppResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to deploy the app", + }, nil + } + + s.log.Infof("Deploy Store app [%s:%s] request request triggered for cluster %s, [org: %s]", + request.AppName, request.Version, request.ClusterID, orgId) + + return &serverpb.DeployStoreAppResponse{ + Status: serverpb.StatusCode_OK, + StatusMessage: "app is successfully deployed", + }, nil +} diff --git a/server/pkg/api/get_cluster.go b/server/pkg/api/get_cluster.go new file mode 100644 index 00000000..90490332 --- /dev/null +++ b/server/pkg/api/get_cluster.go @@ -0,0 +1,84 @@ +package api + +import ( + "context" + "time" + + "github.com/kube-tarian/kad/server/pkg/pb/agentpb" + "github.com/kube-tarian/kad/server/pkg/pb/serverpb" +) + +func (s *Server) GetCluster(ctx context.Context, request *serverpb.GetClusterRequest) ( + *serverpb.GetClusterResponse, error) { + orgId, err := validateRequest(ctx, request.ClusterID) + if err != nil { + s.log.Infof("request validation failed", err) + return &serverpb.GetClusterResponse{ + Status: serverpb.StatusCode_INVALID_ARGUMENT, + StatusMessage: "request validation failed", + }, nil + } + + s.log.Infof("GetCluster request recieved for cluster %s, [org: %s]", orgId, request.ClusterID) + clusterDetails, err := s.serverStore.GetClusterDetails(orgId, request.ClusterID) + if err != nil { + s.log.Errorf("failed to get cluster %s, %v", request.ClusterID, err) + return &serverpb.GetClusterResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed get cluster details", + }, err + } + + resp, err := s.getClusterAppLaunchesFromCacheOrAgent(ctx, orgId, request.ClusterID) + if err != nil || resp == nil || resp.Status != agentpb.StatusCode_OK { + s.log.Error("failed to get cluster application launches from cache/agent: %v", resp) + return &serverpb.GetClusterResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed get cluster app lauches", + }, err + } + + attributes := []*serverpb.ClusterAttribute{} + data := &serverpb.ClusterInfo{ + ClusterID: request.ClusterID, + ClusterName: clusterDetails.ClusterName, + AgentEndpoint: clusterDetails.Endpoint, + Attributes: attributes, + AppLaunchConfigs: mapAgentAppLauncesToServerResp(resp.LaunchConfigList), + } + + s.log.Infof("GetCluster request processed for cluster %s, [org: %s]", request.ClusterID, orgId) + return &serverpb.GetClusterResponse{ + Status: serverpb.StatusCode_OK, + StatusMessage: "get cluster details success", + Data: data, + }, nil +} + +func (s *Server) getClusterAppLaunchesFromCacheOrAgent(ctx context.Context, orgId, clusterID string) ( + *agentpb.GetClusterAppLaunchesResponse, error) { + currentTime := time.Now() + lastFetchedTime := time.Unix(s.orgClusterIDCache[orgId+"-"+clusterID], 0) + + if currentTime.After(lastFetchedTime) { + // cache expired re-trigger the cache + agentClient, aErr := s.agentHandeler.GetAgent(orgId, clusterID) + if aErr == nil { + resp, err := agentClient.GetClient().GetClusterAppLaunches(ctx, &agentpb.GetClusterAppLaunchesRequest{}) + if err == nil { + updateErr := s.serverStore.UpdateClusterAppLaunches(orgId, clusterID, resp.LaunchConfigList) + if updateErr == nil { + s.mutex.Lock() + s.orgClusterIDCache[orgId+"-"+clusterID] = currentTime.Add(delayTimeinMin * time.Minute).Unix() + s.mutex.Unlock() + + return resp, err + } + + } + } + } + + // If any failure happens return from cache. + return s.serverStore.GetClusterAppLaunches(orgId, clusterID) +} diff --git a/server/pkg/api/get_cluster_app.go b/server/pkg/api/get_cluster_app.go new file mode 100644 index 00000000..f843b4ab --- /dev/null +++ b/server/pkg/api/get_cluster_app.go @@ -0,0 +1,13 @@ +package api + +import ( + "context" + "fmt" + + "github.com/kube-tarian/kad/server/pkg/pb/serverpb" +) + +func (s *Server) GetClusterApp(ctx context.Context, request *serverpb.GetClusterAppRequest) ( + *serverpb.GetClusterAppResponse, error) { + return &serverpb.GetClusterAppResponse{}, fmt.Errorf("not implemented") +} diff --git a/server/pkg/api/get_cluster_app_launch_configs.go b/server/pkg/api/get_cluster_app_launch_configs.go new file mode 100644 index 00000000..5e706b4e --- /dev/null +++ b/server/pkg/api/get_cluster_app_launch_configs.go @@ -0,0 +1,32 @@ +package api + +import ( + "context" + + "github.com/kube-tarian/kad/server/pkg/pb/serverpb" +) + +func (s *Server) GetClusterAppLaunchConfigs(ctx context.Context, request *serverpb.GetClusterAppLaunchConfigsRequest) ( + *serverpb.GetClusterAppLaunchConfigsResponse, error) { + orgId, err := validateRequest(ctx, request.ClusterID) + if err != nil { + s.log.Infof("request validation failed", err) + return &serverpb.GetClusterAppLaunchConfigsResponse{ + Status: serverpb.StatusCode_INVALID_ARGUMENT, + StatusMessage: "request validation failed", + }, nil + } + s.log.Infof("GetClusterAppLaunchConfigs request recieved for cluster %s, [org: %s]", request.ClusterID, orgId) + + resp, err := s.getClusterAppLaunchesFromCacheOrAgent(ctx, orgId, request.ClusterID) + if err != nil { + s.log.Error("failed to get cluster application launches from agent", err) + return &serverpb.GetClusterAppLaunchConfigsResponse{Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to get cluster application launches from agent"}, err + } + + s.log.Infof("Fetched %d app launch UIs from the cluster %s, [org: %s]", len(resp.LaunchConfigList), request.ClusterID, orgId) + return &serverpb.GetClusterAppLaunchConfigsResponse{Status: serverpb.StatusCode_OK, + StatusMessage: "successfully fetched the data from agent", + AppLaunchConfig: mapAgentAppLauncesToServerResp(resp.LaunchConfigList)}, nil +} diff --git a/server/pkg/api/get_cluster_apps.go b/server/pkg/api/get_cluster_apps.go new file mode 100644 index 00000000..64919118 --- /dev/null +++ b/server/pkg/api/get_cluster_apps.go @@ -0,0 +1,40 @@ +package api + +import ( + "context" + + "github.com/kube-tarian/kad/server/pkg/pb/agentpb" + "github.com/kube-tarian/kad/server/pkg/pb/serverpb" +) + +func (s *Server) GetClusterApps(ctx context.Context, request *serverpb.GetClusterAppsRequest) ( + *serverpb.GetClusterAppsResponse, error) { + orgId, err := validateRequest(ctx, request.ClusterID) + if err != nil { + s.log.Infof("request validation failed", err) + return &serverpb.GetClusterAppsResponse{ + Status: serverpb.StatusCode_INVALID_ARGUMENT, + StatusMessage: "request validation failed", + }, nil + } + s.log.Infof("GetClusterApps request recieved for cluster %s, [org: %s]", request.ClusterID, orgId) + + a, err := s.agentHandeler.GetAgent(orgId, request.ClusterID) + if err != nil { + s.log.Error("failed to connect to agent", err) + return &serverpb.GetClusterAppsResponse{Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to connect to agent"}, nil + } + + resp, err := a.GetClient().GetClusterApps(ctx, &agentpb.GetClusterAppsRequest{}) + if err != nil { + s.log.Error("failed to get cluster application from agent", err) + return &serverpb.GetClusterAppsResponse{Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to get cluster application from agent"}, nil + } + + s.log.Infof("Fetched %d installed apps from the cluster %s, [org: %s]", len(resp.AppData), request.ClusterID, orgId) + return &serverpb.GetClusterAppsResponse{Status: serverpb.StatusCode_OK, + StatusMessage: "successfully fetched the data from agent", + AppConfigs: mapAgentAppsToServerResp(resp.AppData)}, nil +} diff --git a/server/pkg/api/get_clusters.go b/server/pkg/api/get_clusters.go new file mode 100644 index 00000000..165f5df5 --- /dev/null +++ b/server/pkg/api/get_clusters.go @@ -0,0 +1,54 @@ +package api + +import ( + "context" + + "github.com/kube-tarian/kad/server/pkg/pb/serverpb" +) + +func (s *Server) GetClusters(ctx context.Context, request *serverpb.GetClustersRequest) ( + *serverpb.GetClustersResponse, error) { + orgId, err := validateRequest(ctx) + if err != nil { + s.log.Infof("request validation failed", err) + return &serverpb.GetClustersResponse{ + Status: serverpb.StatusCode_INVALID_ARGUMENT, + StatusMessage: "request validation failed", + }, nil + } + + s.log.Infof("GetClusters request recieved, [org: %s]", orgId) + clusterDetails, err := s.serverStore.GetClusters(orgId) + if err != nil { + s.log.Errorf("failed to get clusters, %v", err) + return &serverpb.GetClustersResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed get cluster details", + }, err + } + + var data []*serverpb.ClusterInfo + for _, cluster := range clusterDetails { + resp, err := s.getClusterAppLaunchesFromCacheOrAgent(ctx, orgId, cluster.ClusterID) + if err != nil { + s.log.Errorf("failed to get cluster appLaunches for cluster: %s, %v", cluster.ClusterID, err) + continue + } + + attributes := []*serverpb.ClusterAttribute{} + data = append(data, &serverpb.ClusterInfo{ + ClusterID: cluster.ClusterID, + ClusterName: cluster.ClusterName, + AgentEndpoint: cluster.Endpoint, + Attributes: attributes, + AppLaunchConfigs: mapAgentAppLauncesToServerResp(resp.LaunchConfigList), + }) + } + + s.log.Infof("Fetched %d clusters, [org: %s]", len(data), orgId) + return &serverpb.GetClustersResponse{ + Status: serverpb.StatusCode_OK, + StatusMessage: "get cluster details success", + Data: data, + }, nil +} diff --git a/server/pkg/api/get_store_app.go b/server/pkg/api/get_store_app.go new file mode 100644 index 00000000..58564b17 --- /dev/null +++ b/server/pkg/api/get_store_app.go @@ -0,0 +1,54 @@ +package api + +import ( + "context" + "encoding/hex" + + "github.com/kube-tarian/kad/server/pkg/pb/serverpb" +) + +func (s *Server) GetStoreApp(ctx context.Context, request *serverpb.GetStoreAppRequest) ( + *serverpb.GetStoreAppResponse, error) { + _, err := validateRequest(ctx, request.AppName, request.Version) + if err != nil { + s.log.Infof("request validation failed", err) + return &serverpb.GetStoreAppResponse{ + Status: serverpb.StatusCode_INVALID_ARGUMENT, + StatusMessage: "request validation failed", + }, nil + } + + config, err := s.serverStore.GetAppFromStore(request.AppName, request.Version) + if err != nil { + s.log.Errorf("failed to get app config from store, %v", err) + return &serverpb.GetStoreAppResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to get app config from store", + }, nil + } + + decodedIconBytes, _ := hex.DecodeString(config.Icon) + appConfig := &serverpb.StoreAppConfig{ + AppName: config.Name, + Version: config.Version, + Category: config.Category, + Description: config.Description, + ChartName: config.ChartName, + RepoName: config.RepoName, + RepoURL: config.RepoURL, + Namespace: config.Namespace, + CreateNamespace: config.CreateNamespace, + PrivilegedNamespace: config.PrivilegedNamespace, + Icon: decodedIconBytes, + LaunchURL: config.LaunchURL, + LaunchUIDescription: config.LaunchUIDescription, + ReleaseName: config.ReleaseName, + } + + return &serverpb.GetStoreAppResponse{ + Status: serverpb.StatusCode_OK, + StatusMessage: "app config is sucessfuly fetched from store", + AppConfig: appConfig, + }, nil + +} diff --git a/server/pkg/api/get_store_app_values.go b/server/pkg/api/get_store_app_values.go new file mode 100644 index 00000000..50e7de58 --- /dev/null +++ b/server/pkg/api/get_store_app_values.go @@ -0,0 +1,56 @@ +package api + +import ( + "context" + + "github.com/kube-tarian/kad/server/pkg/pb/serverpb" + "gopkg.in/yaml.v2" +) + +func (s *Server) GetStoreAppValues(ctx context.Context, request *serverpb.GetStoreAppValuesRequest) ( + *serverpb.GetStoreAppValuesResponse, error) { + orgId, err := validateRequest(ctx, request.ClusterID) + if err != nil { + s.log.Infof("request validation failed", err) + return &serverpb.GetStoreAppValuesResponse{ + Status: serverpb.StatusCode_INVALID_ARGUMENT, + StatusMessage: "request validation failed", + }, nil + } + s.log.Infof("Get store app [%s:%s] values request for cluster %s recieved, [org: %s]", + request.AppName, request.Version, request.ClusterID, orgId) + + config, err := s.serverStore.GetAppFromStore(request.AppName, request.Version) + if err != nil { + s.log.Errorf("failed to get store app values, %v", err) + return &serverpb.GetStoreAppValuesResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to get store app values", + }, nil + } + + marshaledOverride, err := yaml.Marshal(config.OverrideValues) + if err != nil { + return &serverpb.GetStoreAppValuesResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to marshal values", + }, nil + } + + overrideValues, err := s.replaceGlobalValues(orgId, request.ClusterID, decodeBase64StringToBytes(string(marshaledOverride))) + if err != nil { + s.log.Errorf("failed to update overrided store app values, %v", err) + return &serverpb.GetStoreAppValuesResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to update overrided store app values", + }, nil + } + + s.log.Infof("Get store app [%s:%s] values request for cluster %s successful, [org: %s]", + request.AppName, request.Version, request.ClusterID, orgId) + return &serverpb.GetStoreAppValuesResponse{ + Status: serverpb.StatusCode_OK, + StatusMessage: "store app values sucessfuly fetched", + OverrideValues: overrideValues, + }, nil +} diff --git a/server/pkg/api/get_store_apps.go b/server/pkg/api/get_store_apps.go new file mode 100644 index 00000000..d7a39f95 --- /dev/null +++ b/server/pkg/api/get_store_apps.go @@ -0,0 +1,59 @@ +package api + +import ( + "context" + "encoding/hex" + + "github.com/kube-tarian/kad/server/pkg/pb/serverpb" +) + +func (s *Server) GetStoreApps(ctx context.Context, request *serverpb.GetStoreAppsRequest) ( + *serverpb.GetStoreAppsResponse, error) { + _, err := validateRequest(ctx) + if err != nil { + s.log.Infof("request validation failed", err) + return &serverpb.GetStoreAppsResponse{ + Status: serverpb.StatusCode_INVALID_ARGUMENT, + StatusMessage: "request validation failed", + }, nil + } + + configs, err := s.serverStore.GetAppsFromStore() + if err != nil { + s.log.Errorf("failed to get app config's from store, %v", err) + return &serverpb.GetStoreAppsResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to get app config's from store", + }, nil + } + + appsData := []*serverpb.StoreAppsData{} + for _, config := range *configs { + decodedIconBytes, _ := hex.DecodeString(config.Icon) + appsData = append(appsData, &serverpb.StoreAppsData{ + AppConfigs: &serverpb.StoreAppConfig{ + AppName: config.Name, + Version: config.Version, + Category: config.Category, + Description: config.Description, + ChartName: config.ChartName, + RepoName: config.RepoName, + RepoURL: config.RepoURL, + Namespace: config.Namespace, + CreateNamespace: config.CreateNamespace, + PrivilegedNamespace: config.PrivilegedNamespace, + Icon: decodedIconBytes, + LaunchURL: config.LaunchURL, + LaunchUIDescription: config.LaunchUIDescription, + ReleaseName: config.ReleaseName, + }, + OverrideValues: config.OverrideValues, + }) + } + + return &serverpb.GetStoreAppsResponse{ + Status: serverpb.StatusCode_OK, + StatusMessage: "app config's are sucessfuly fetched from store", + Data: appsData, + }, nil +} diff --git a/server/pkg/api/new_cluster_registration.go b/server/pkg/api/new_cluster_registration.go new file mode 100644 index 00000000..5a4f56f6 --- /dev/null +++ b/server/pkg/api/new_cluster_registration.go @@ -0,0 +1,87 @@ +package api + +import ( + "context" + + "github.com/gocql/gocql" + "github.com/kube-tarian/kad/server/pkg/agent" + "github.com/kube-tarian/kad/server/pkg/credential" + "github.com/kube-tarian/kad/server/pkg/pb/serverpb" +) + +func (s *Server) NewClusterRegistration(ctx context.Context, request *serverpb.NewClusterRegistrationRequest) ( + *serverpb.NewClusterRegistrationResponse, error) { + orgId, err := validateRequest(ctx) + if err != nil { + s.log.Infof("request validation failed", err) + return &serverpb.NewClusterRegistrationResponse{ + Status: serverpb.StatusCode_INVALID_ARGUMENT, + StatusMessage: "request validation failed", + }, nil + } + + clusterID := gocql.TimeUUID().String() + s.log.Infof("New cluster registration request for cluster %s recieved, clusterId: %s, [org: %s]", + request.ClusterName, clusterID, orgId) + + caData, caDataErr := getBase64DecodedString(request.ClientCAChainData) + clientKey, clientKeyErr := getBase64DecodedString(request.ClientKeyData) + clientCrt, clientCrtErr := getBase64DecodedString(request.ClientCertData) + if caDataErr != nil || clientKeyErr != nil || clientCrtErr != nil { + return &serverpb.NewClusterRegistrationResponse{ + Status: serverpb.StatusCode_INVALID_ARGUMENT, + StatusMessage: "only base64 encoded certificates are allowed", + }, nil + } + + agentConfig := &agent.Config{ + ClusterName: request.ClusterName, + Address: request.AgentEndpoint, + CaCert: caData, + Key: clientKey, + Cert: clientCrt, + } + if err := s.agentHandeler.AddAgent(clusterID, agentConfig); err != nil { + s.log.Errorf("failed to connect to agent on cluster %s, %v", request.ClusterName, err) + return &serverpb.NewClusterRegistrationResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to connect to agent", + }, nil + } + + err = credential.PutClusterCerts(ctx, clusterID, caData, clientKey, clientCrt) + if err != nil { + s.log.Errorf("failed to store cert in vault for cluster %s, %v", clusterID, err) + return &serverpb.NewClusterRegistrationResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to store cluster credentials", + }, nil + } + + err = s.serverStore.AddCluster(orgId, clusterID, request.ClusterName, request.AgentEndpoint) + if err != nil { + s.log.Errorf("failed to store cluster %s to db, %v", request.ClusterName, err) + return &serverpb.NewClusterRegistrationResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to store cluster registration", + }, nil + } + + if s.cfg.RegisterLaunchAppsConifg { + if err := s.configureSSOForClusterApps(ctx, orgId, clusterID); err != nil { + s.log.Errorf("%v", err) + return &serverpb.NewClusterRegistrationResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to configure SSO for cluster apps", + }, nil + } + } + + s.log.Infof("New cluster registration request for cluster %s successful, clusterId: %s, [org: %s]", + request.ClusterName, clusterID, orgId) + return &serverpb.NewClusterRegistrationResponse{ + Status: serverpb.StatusCode_OK, + StatusMessage: "register cluster success", + ClusterID: clusterID, + }, nil +} diff --git a/server/pkg/api/server.go b/server/pkg/api/server.go index 8fecee51..c32e65ee 100644 --- a/server/pkg/api/server.go +++ b/server/pkg/api/server.go @@ -77,3 +77,11 @@ func decodeBase64StringToBytes(val string) []byte { dval, _ := base64.StdEncoding.DecodeString(val) return dval } + +func getBase64DecodedString(encodedString string) (string, error) { + decodedByte, err := base64.StdEncoding.DecodeString(encodedString) + if err != nil { + return "", err + } + return string(decodedByte), nil +} diff --git a/server/pkg/api/store_apps.go b/server/pkg/api/store_apps.go deleted file mode 100644 index 481ced5d..00000000 --- a/server/pkg/api/store_apps.go +++ /dev/null @@ -1,558 +0,0 @@ -package api - -import ( - "bytes" - "context" - "encoding/hex" - "fmt" - "text/template" - - "github.com/kube-tarian/kad/server/pkg/pb/agentpb" - "github.com/kube-tarian/kad/server/pkg/pb/serverpb" - "github.com/kube-tarian/kad/server/pkg/types" - "github.com/pkg/errors" - "gopkg.in/yaml.v2" -) - -func (s *Server) AddStoreApp(ctx context.Context, request *serverpb.AddStoreAppRequest) ( - *serverpb.AddStoreAppResponse, error) { - if request.AppConfig.AppName == "" || request.AppConfig.Version == "" { - s.log.Infof("AppName or version is missing for add store app request") - return &serverpb.AddStoreAppResponse{ - Status: serverpb.StatusCode_INVALID_ARGUMENT, - StatusMessage: "AppName or version is missing in the request", - }, nil - } - - config := &types.StoreAppConfig{ - ReleaseName: request.AppConfig.ReleaseName, - AppName: request.AppConfig.AppName, - Version: request.AppConfig.Version, - Category: request.AppConfig.Category, - Description: request.AppConfig.Description, - ChartName: request.AppConfig.ChartName, - RepoName: request.AppConfig.RepoName, - RepoURL: request.AppConfig.RepoURL, - Namespace: request.AppConfig.Namespace, - CreateNamespace: request.AppConfig.CreateNamespace, - PrivilegedNamespace: request.AppConfig.PrivilegedNamespace, - Icon: hex.EncodeToString(request.AppConfig.Icon), - LaunchURL: request.AppConfig.LaunchURL, - LaunchUIDescription: request.AppConfig.LaunchUIDescription, - OverrideValues: encodeBase64BytesToString(request.AppValues.OverrideValues), - LaunchUIValues: encodeBase64BytesToString(request.AppValues.LaunchUIValues), - TemplateValues: encodeBase64BytesToString(request.AppValues.TemplateValues), - } - - if err := s.serverStore.AddOrUpdateStoreApp(config); err != nil { - s.log.Errorf("failed to add app config to store, %v", err) - return &serverpb.AddStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed add app config to store", - }, nil - } - - return &serverpb.AddStoreAppResponse{ - Status: serverpb.StatusCode_OK, - StatusMessage: "app config is sucessfuly added to store", - }, nil -} - -func (s *Server) UpdateStoreApp(ctx context.Context, request *serverpb.UpdateStoreAppRequest) ( - *serverpb.UpdateStoreAppRsponse, error) { - if request.AppConfig.AppName == "" || request.AppConfig.Version == "" { - s.log.Infof("AppName or version is missing for update store app request") - return &serverpb.UpdateStoreAppRsponse{ - Status: serverpb.StatusCode_INVALID_ARGUMENT, - StatusMessage: "AppName or version is missing in the request", - }, nil - } - - config := &types.StoreAppConfig{ - ReleaseName: request.AppConfig.ReleaseName, - AppName: request.AppConfig.AppName, - Version: request.AppConfig.Version, - Category: request.AppConfig.Category, - Description: request.AppConfig.Description, - ChartName: request.AppConfig.ChartName, - RepoName: request.AppConfig.RepoName, - RepoURL: request.AppConfig.RepoURL, - Namespace: request.AppConfig.Namespace, - CreateNamespace: request.AppConfig.CreateNamespace, - PrivilegedNamespace: request.AppConfig.PrivilegedNamespace, - Icon: hex.EncodeToString(request.AppConfig.Icon), - LaunchURL: request.AppConfig.LaunchURL, - LaunchUIDescription: request.AppConfig.LaunchUIDescription, - OverrideValues: encodeBase64BytesToString(request.AppValues.OverrideValues), - LaunchUIValues: encodeBase64BytesToString(request.AppValues.LaunchUIValues), - TemplateValues: encodeBase64BytesToString(request.AppValues.TemplateValues), - } - - if err := s.serverStore.AddOrUpdateStoreApp(config); err != nil { - s.log.Errorf("failed to update app config in store, %v", err) - return &serverpb.UpdateStoreAppRsponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to update app config in store", - }, nil - } - - return &serverpb.UpdateStoreAppRsponse{ - Status: serverpb.StatusCode_OK, - StatusMessage: "app config is sucessfuly updated", - }, nil -} - -func (s *Server) DeleteStoreApp(ctx context.Context, request *serverpb.DeleteStoreAppRequest) ( - *serverpb.DeleteStoreAppResponse, error) { - if request.AppName == "" || request.Version == "" { - s.log.Infof("AppName or version is missing for delete store app request") - return &serverpb.DeleteStoreAppResponse{ - Status: serverpb.StatusCode_INVALID_ARGUMENT, - StatusMessage: "AppName or version is missing in the request", - }, nil - } - - if err := s.serverStore.DeleteAppInStore(request.AppName, request.Version); err != nil { - s.log.Errorf("failed to delete app config from store, %v", err) - return &serverpb.DeleteStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to delete app config from store", - }, nil - } - - return &serverpb.DeleteStoreAppResponse{ - Status: serverpb.StatusCode_OK, - StatusMessage: "app config is sucessfuly deleted", - }, nil - -} - -func (s *Server) GetStoreApp(ctx context.Context, request *serverpb.GetStoreAppRequest) ( - *serverpb.GetStoreAppResponse, error) { - if request.AppName == "" || request.Version == "" { - s.log.Infof("AppName or version is missing for get store app request") - return &serverpb.GetStoreAppResponse{ - Status: serverpb.StatusCode_INVALID_ARGUMENT, - StatusMessage: "AppName or version is missing in the request", - }, nil - } - - config, err := s.serverStore.GetAppFromStore(request.AppName, request.Version) - if err != nil { - s.log.Errorf("failed to get app config from store, %v", err) - return &serverpb.GetStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to get app config from store", - }, nil - } - - decodedIconBytes, _ := hex.DecodeString(config.Icon) - appConfig := &serverpb.StoreAppConfig{ - AppName: config.Name, - Version: config.Version, - Category: config.Category, - Description: config.Description, - ChartName: config.ChartName, - RepoName: config.RepoName, - RepoURL: config.RepoURL, - Namespace: config.Namespace, - CreateNamespace: config.CreateNamespace, - PrivilegedNamespace: config.PrivilegedNamespace, - Icon: decodedIconBytes, - LaunchURL: config.LaunchURL, - LaunchUIDescription: config.LaunchUIDescription, - ReleaseName: config.ReleaseName, - } - - return &serverpb.GetStoreAppResponse{ - Status: serverpb.StatusCode_OK, - StatusMessage: "app config is sucessfuly fetched from store", - AppConfig: appConfig, - }, nil - -} - -func (s *Server) GetStoreApps(ctx context.Context, request *serverpb.GetStoreAppsRequest) ( - *serverpb.GetStoreAppsResponse, error) { - - configs, err := s.serverStore.GetAppsFromStore() - if err != nil { - s.log.Errorf("failed to get app config's from store, %v", err) - return &serverpb.GetStoreAppsResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to get app config's from store", - }, nil - } - - appsData := []*serverpb.StoreAppsData{} - for _, config := range *configs { - decodedIconBytes, _ := hex.DecodeString(config.Icon) - appsData = append(appsData, &serverpb.StoreAppsData{ - AppConfigs: &serverpb.StoreAppConfig{ - AppName: config.Name, - Version: config.Version, - Category: config.Category, - Description: config.Description, - ChartName: config.ChartName, - RepoName: config.RepoName, - RepoURL: config.RepoURL, - Namespace: config.Namespace, - CreateNamespace: config.CreateNamespace, - PrivilegedNamespace: config.PrivilegedNamespace, - Icon: decodedIconBytes, - LaunchURL: config.LaunchURL, - LaunchUIDescription: config.LaunchUIDescription, - ReleaseName: config.ReleaseName, - }, - OverrideValues: config.OverrideValues, - }) - } - - return &serverpb.GetStoreAppsResponse{ - Status: serverpb.StatusCode_OK, - StatusMessage: "app config's are sucessfuly fetched from store", - Data: appsData, - }, nil -} - -func (s *Server) GetStoreAppValues(ctx context.Context, request *serverpb.GetStoreAppValuesRequest) ( - *serverpb.GetStoreAppValuesResponse, error) { - metadataMap := metadataContextToMap(ctx) - orgId := metadataMap[organizationIDAttribute] - if orgId == "" { - s.log.Errorf("organization ID is missing in the request") - return &serverpb.GetStoreAppValuesResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "Organization Id is missing", - }, nil - } - s.log.Infof("[org: %s] Get store app [%s:%s] values request for cluster %s recieved", orgId, - request.AppName, request.Version, request.ClusterID) - - config, err := s.serverStore.GetAppFromStore(request.AppName, request.Version) - if err != nil { - s.log.Errorf("failed to get store app values, %v", err) - return &serverpb.GetStoreAppValuesResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to get store app values", - }, nil - } - - marshaledOverride, err := yaml.Marshal(config.OverrideValues) - if err != nil { - return &serverpb.GetStoreAppValuesResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to marshal values", - }, nil - } - - overrideValues, err := s.replaceGlobalValues(orgId, request.ClusterID, decodeBase64StringToBytes(string(marshaledOverride))) - if err != nil { - s.log.Errorf("failed to update overrided store app values, %v", err) - return &serverpb.GetStoreAppValuesResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to update overrided store app values", - }, nil - } - - return &serverpb.GetStoreAppValuesResponse{ - Status: serverpb.StatusCode_OK, - StatusMessage: "store app values sucessfuly fetched", - OverrideValues: overrideValues, - }, nil -} - -func (s *Server) replaceGlobalValues(orgId, clusterID string, overridedValues []byte) ([]byte, error) { - agent, err := s.agentHandeler.GetAgent(orgId, clusterID) - if err != nil { - return nil, errors.WithMessagef(err, "failed to initialize agent for cluster %s", clusterID) - } - resp, err := agent.GetClient().GetClusterGlobalValues(context.TODO(), &agentpb.GetClusterGlobalValuesRequest{}) - if err != nil { - return nil, err - } - if resp.Status != agentpb.StatusCode_OK { - return nil, fmt.Errorf("failed to get global values for cluster %s", clusterID) - } - - var globalValues map[string]interface{} - err = yaml.Unmarshal(resp.GlobalValues, &globalValues) - if err != nil { - return nil, errors.WithMessagef(err, "failed to unmarshal cluster values") - } - - var overrideValues map[string]interface{} - err = yaml.Unmarshal(overridedValues, &overrideValues) - if err != nil { - return nil, errors.WithMessagef(err, "failed to unmarshal override values") - } - - return replaceOverrideGlobalValues(overrideValues, globalValues) - -} - -func replaceOverrideGlobalValues(overrideValues map[string]interface{}, - globlaValues map[string]interface{}) (transformedData []byte, err error) { - yamlData, err := yaml.Marshal(overrideValues) - if err != nil { - return - } - - tmpl, err := template.New("templateVal").Parse(string(yamlData)) - if err != nil { - return - } - - var buf bytes.Buffer - err = tmpl.Execute(&buf, globlaValues) - if err != nil { - return - } - - transformedData = buf.Bytes() - return -} - -func (s *Server) DeployStoreApp(ctx context.Context, request *serverpb.DeployStoreAppRequest) ( - *serverpb.DeployStoreAppResponse, error) { - metadataMap := metadataContextToMap(ctx) - orgId := metadataMap[organizationIDAttribute] - if orgId == "" { - s.log.Errorf("organization ID is missing in the request") - return &serverpb.DeployStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "Organization Id is missing", - }, nil - } - s.log.Infof("[org: %s] Deploy store app [%s:%s] request for cluster %s recieved", orgId, - request.AppName, request.Version, request.ClusterID) - - config, err := s.serverStore.GetAppFromStore(request.AppName, request.Version) - if err != nil { - s.log.Errorf("failed to get store app values, %v", err) - return &serverpb.DeployStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to find store app values", - }, nil - } - - marshaledOverride, err := yaml.Marshal(config.OverrideValues) - if err != nil { - return &serverpb.DeployStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to find store app values", - }, nil - } - - marshaledLaunchUi, err := yaml.Marshal(config.LaunchUIValues) - if err != nil { - return &serverpb.DeployStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to find store app values", - }, nil - } - - decodedIconBytes, _ := hex.DecodeString(config.Icon) - req := &agentpb.InstallAppRequest{ - AppConfig: &agentpb.AppConfig{ - AppName: config.Name, - Version: config.Version, - ReleaseName: config.ReleaseName, - Category: config.Category, - Description: config.Description, - ChartName: config.ChartName, - RepoName: config.RepoName, - RepoURL: config.RepoURL, - Namespace: config.Namespace, - CreateNamespace: config.CreateNamespace, - PrivilegedNamespace: config.PrivilegedNamespace, - Icon: decodedIconBytes, - LaunchURL: config.LaunchURL, - LaunchUIDescription: config.LaunchUIDescription, - DefualtApp: false, - }, - AppValues: &agentpb.AppValues{ - OverrideValues: request.OverrideValues, - LaunchUIValues: decodeBase64StringToBytes(string(marshaledLaunchUi)), - TemplateValues: decodeBase64StringToBytes(string(marshaledOverride)), - }, - } - - agent, err := s.agentHandeler.GetAgent(orgId, request.ClusterID) - if err != nil { - s.log.Errorf("failed to initialize agent, %v", err) - return &serverpb.DeployStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to deploy the app", - }, nil - } - - _, err = agent.GetClient().InstallApp(ctx, req) - if err != nil { - s.log.Errorf("failed to deploy app, %v", err) - return &serverpb.DeployStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to deploy the app", - }, nil - } - - s.log.Infof("[org: %s] Store app [%s:%s] request request triggered for cluster %s", orgId, - request.AppName, request.Version, request.ClusterID) - - return &serverpb.DeployStoreAppResponse{ - Status: serverpb.StatusCode_OK, - StatusMessage: "app is successfully deployed", - }, nil -} - -func (s *Server) UpgradeStoreApp(ctx context.Context, request *serverpb.UpgradeStoreAppRequest) ( - *serverpb.UpgradeStoreAppResponse, error) { - metadataMap := metadataContextToMap(ctx) - orgId := metadataMap[organizationIDAttribute] - if orgId == "" { - s.log.Errorf("organization ID is missing in the request") - return &serverpb.UpgradeStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "Organization Id is missing", - }, nil - } - - s.log.Infof("[org: %s] Deploy store app [%s:%s] request for cluster %s recieved", orgId, - request.AppName, request.Version, request.ClusterID) - - config, err := s.serverStore.GetAppFromStore(request.AppName, request.Version) - if err != nil { - s.log.Errorf("failed to get store app values, %v", err) - return &serverpb.UpgradeStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to find store app values", - }, nil - } - - marshaledOverride, err := yaml.Marshal(config.OverrideValues) - if err != nil { - return &serverpb.UpgradeStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to find store app values", - }, nil - } - - marshaledLaunchUi, err := yaml.Marshal(config.LaunchUIValues) - if err != nil { - return &serverpb.UpgradeStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to find store app values", - }, nil - } - - decodedIconBytes, _ := hex.DecodeString(config.Icon) - req := &agentpb.UpgradeAppRequest{ - AppConfig: &agentpb.AppConfig{ - AppName: config.Name, - Version: config.Version, - ReleaseName: config.ReleaseName, - Category: config.Category, - Description: config.Description, - ChartName: config.ChartName, - RepoName: config.RepoName, - RepoURL: config.RepoURL, - Namespace: config.Namespace, - CreateNamespace: config.CreateNamespace, - PrivilegedNamespace: config.PrivilegedNamespace, - Icon: decodedIconBytes, - LaunchURL: config.LaunchURL, - LaunchUIDescription: config.LaunchUIDescription, - DefualtApp: false, - }, - AppValues: &agentpb.AppValues{ - OverrideValues: request.OverrideValues, - LaunchUIValues: decodeBase64StringToBytes(string(marshaledLaunchUi)), - TemplateValues: decodeBase64StringToBytes(string(marshaledOverride)), - }, - } - - agent, err := s.agentHandeler.GetAgent(orgId, request.ClusterID) - if err != nil { - s.log.Errorf("failed to initialize agent, %v", err) - return &serverpb.UpgradeStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to deploy the app", - }, nil - } - - _, err = agent.GetClient().UpgradeApp(ctx, req) - if err != nil { - s.log.Errorf("failed to deploy app, %v", err) - return &serverpb.UpgradeStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to deploy the app", - }, nil - } - - s.log.Infof("[org: %s] Store app [%s:%s] request request triggered for cluster %s", orgId, - request.AppName, request.Version, request.ClusterID) - - return &serverpb.UpgradeStoreAppResponse{ - Status: serverpb.StatusCode_OK, - StatusMessage: "app is successfully deployed", - }, nil -} - -func (s *Server) UnDeployStoreApp(ctx context.Context, request *serverpb.UnDeployStoreAppRequest) ( - *serverpb.UnDeployStoreAppResponse, error) { - metadataMap := metadataContextToMap(ctx) - orgId := metadataMap[organizationIDAttribute] - if orgId == "" { - s.log.Errorf("organization ID is missing in the request") - return &serverpb.UnDeployStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "Organization Id is missing", - }, nil - } - if request.ClusterID == "" { - s.log.Errorf("Cluster Id is missing in the request") - return &serverpb.UnDeployStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "Cluster Id is missing", - }, nil - } - if request.ReleaseName == "" { - s.log.Errorf("Release name is missing in the request") - return &serverpb.UnDeployStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "Release name is missing", - }, nil - } - - s.log.Infof("[org: %s] Un Deploy store app %s request for cluster %s recieved", orgId, - request.ReleaseName, request.ClusterID) - - agent, err := s.agentHandeler.GetAgent(orgId, request.ClusterID) - if err != nil { - s.log.Errorf("failed to initialize agent, %v", err) - return &serverpb.UnDeployStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to undeploy the app", - }, nil - } - - req := &agentpb.UnInstallAppRequest{ - ReleaseName: request.ReleaseName, - } - resp, err := agent.GetClient().UnInstallApp(ctx, req) - if err != nil { - s.log.Errorf("failed to undeploy app, %v", err) - return &serverpb.UnDeployStoreAppResponse{ - Status: serverpb.StatusCode_INTERNRAL_ERROR, - StatusMessage: "failed to undeploy the app", - }, nil - } - - s.log.Infof("[org: %s] Store app %s request request triggered for cluster %s", orgId, - request.ReleaseName, request.ClusterID) - - return &serverpb.UnDeployStoreAppResponse{ - Status: serverpb.StatusCode(resp.Status), - StatusMessage: resp.StatusMessage, - }, nil -} diff --git a/server/pkg/api/undeploy_store_app.go b/server/pkg/api/undeploy_store_app.go new file mode 100644 index 00000000..4130cb7e --- /dev/null +++ b/server/pkg/api/undeploy_store_app.go @@ -0,0 +1,52 @@ +package api + +import ( + "context" + + "github.com/kube-tarian/kad/server/pkg/pb/agentpb" + "github.com/kube-tarian/kad/server/pkg/pb/serverpb" +) + +func (s *Server) UnDeployStoreApp(ctx context.Context, request *serverpb.UnDeployStoreAppRequest) ( + *serverpb.UnDeployStoreAppResponse, error) { + orgId, err := validateRequest(ctx, request.ClusterID, request.ReleaseName) + if err != nil { + s.log.Infof("request validation failed", err) + return &serverpb.UnDeployStoreAppResponse{ + Status: serverpb.StatusCode_INVALID_ARGUMENT, + StatusMessage: "request validation failed", + }, nil + } + + s.log.Infof("UnDeploy store app %s request for cluster %s recieved, [org: %s]", + request.ReleaseName, request.ClusterID, orgId) + + agent, err := s.agentHandeler.GetAgent(orgId, request.ClusterID) + if err != nil { + s.log.Errorf("failed to initialize agent, %v", err) + return &serverpb.UnDeployStoreAppResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to undeploy the app", + }, nil + } + + req := &agentpb.UnInstallAppRequest{ + ReleaseName: request.ReleaseName, + } + resp, err := agent.GetClient().UnInstallApp(ctx, req) + if err != nil { + s.log.Errorf("failed to undeploy app, %v", err) + return &serverpb.UnDeployStoreAppResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to undeploy the app", + }, nil + } + + s.log.Infof("UnDeploy store app %s request request triggered for cluster %s, [org: %s]", + request.ReleaseName, request.ClusterID, orgId) + + return &serverpb.UnDeployStoreAppResponse{ + Status: serverpb.StatusCode(resp.Status), + StatusMessage: resp.StatusMessage, + }, nil +} diff --git a/server/pkg/api/update_cluster_registration.go b/server/pkg/api/update_cluster_registration.go new file mode 100644 index 00000000..80971d48 --- /dev/null +++ b/server/pkg/api/update_cluster_registration.go @@ -0,0 +1,72 @@ +package api + +import ( + "context" + + "github.com/kube-tarian/kad/server/pkg/agent" + "github.com/kube-tarian/kad/server/pkg/credential" + "github.com/kube-tarian/kad/server/pkg/pb/serverpb" +) + +func (s *Server) UpdateClusterRegistration(ctx context.Context, request *serverpb.UpdateClusterRegistrationRequest) ( + *serverpb.UpdateClusterRegistrationResponse, error) { + orgId, err := validateRequest(ctx, request.ClusterID) + if err != nil { + s.log.Infof("request validation failed", err) + return &serverpb.UpdateClusterRegistrationResponse{ + Status: serverpb.StatusCode_INVALID_ARGUMENT, + StatusMessage: "request validation failed", + }, nil + } + s.log.Infof("Update cluster registration request for cluster %s recieved, [org: %s]", request.ClusterName, orgId) + + caData, caDataErr := getBase64DecodedString(request.ClientCAChainData) + clientKey, clientKeyErr := getBase64DecodedString(request.ClientKeyData) + clientCrt, clientCrtErr := getBase64DecodedString(request.ClientCertData) + if caDataErr != nil || clientKeyErr != nil || clientCrtErr != nil { + return &serverpb.UpdateClusterRegistrationResponse{ + Status: serverpb.StatusCode_INVALID_ARGUMENT, + StatusMessage: "only base64 encoded certificates are allowed", + }, nil + } + + agentConfig := &agent.Config{ + ClusterName: request.ClusterName, + Address: request.AgentEndpoint, + CaCert: caData, + Key: clientKey, + Cert: clientCrt, + } + + if err := s.agentHandeler.UpdateAgent(request.ClusterID, agentConfig); err != nil { + s.log.Errorf("[org: %s] failed to connect to agent on cluster %s, %v", orgId, request.ClusterName, err) + return &serverpb.UpdateClusterRegistrationResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to connect to agent", + }, nil + } + + err = credential.PutClusterCerts(ctx, request.ClusterID, caData, clientKey, clientCrt) + if err != nil { + s.log.Errorf("[org: %s] failed to update cert in vault for cluster %s, %v", orgId, request.ClusterID, err) + return &serverpb.UpdateClusterRegistrationResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed update register cluster", + }, nil + } + + err = s.serverStore.UpdateCluster(orgId, request.ClusterID, request.ClusterName, request.AgentEndpoint) + if err != nil { + s.log.Errorf("[org: %s] failed to update cluster %s in db, %v", orgId, request.ClusterName, err) + return &serverpb.UpdateClusterRegistrationResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed update register cluster", + }, nil + } + + s.log.Infof("Update cluster registration request for cluster %s successful, [org: %s]", request.ClusterName, orgId) + return &serverpb.UpdateClusterRegistrationResponse{ + Status: serverpb.StatusCode_OK, + StatusMessage: "cluster register update success", + }, nil +} diff --git a/server/pkg/api/update_store_app.go b/server/pkg/api/update_store_app.go new file mode 100644 index 00000000..955c5fb4 --- /dev/null +++ b/server/pkg/api/update_store_app.go @@ -0,0 +1,57 @@ +package api + +import ( + "context" + "encoding/hex" + + "github.com/kube-tarian/kad/server/pkg/pb/serverpb" + "github.com/kube-tarian/kad/server/pkg/types" +) + +func (s *Server) UpdateStoreApp(ctx context.Context, request *serverpb.UpdateStoreAppRequest) ( + *serverpb.UpdateStoreAppRsponse, error) { + _, err := validateRequest(ctx, request.AppConfig.AppName, request.AppConfig.Version) + if err != nil { + s.log.Infof("request validation failed", err) + return &serverpb.UpdateStoreAppRsponse{ + Status: serverpb.StatusCode_INVALID_ARGUMENT, + StatusMessage: "request validation failed", + }, nil + } + s.log.Infof("Update store app [%s:%s] request recieved", request.AppConfig.AppName, request.AppConfig.Version) + + //TODO check store app exist in DB + config := &types.StoreAppConfig{ + ReleaseName: request.AppConfig.ReleaseName, + AppName: request.AppConfig.AppName, + Version: request.AppConfig.Version, + Category: request.AppConfig.Category, + Description: request.AppConfig.Description, + ChartName: request.AppConfig.ChartName, + RepoName: request.AppConfig.RepoName, + RepoURL: request.AppConfig.RepoURL, + Namespace: request.AppConfig.Namespace, + CreateNamespace: request.AppConfig.CreateNamespace, + PrivilegedNamespace: request.AppConfig.PrivilegedNamespace, + Icon: hex.EncodeToString(request.AppConfig.Icon), + LaunchURL: request.AppConfig.LaunchURL, + LaunchUIDescription: request.AppConfig.LaunchUIDescription, + OverrideValues: encodeBase64BytesToString(request.AppValues.OverrideValues), + LaunchUIValues: encodeBase64BytesToString(request.AppValues.LaunchUIValues), + TemplateValues: encodeBase64BytesToString(request.AppValues.TemplateValues), + } + + if err := s.serverStore.AddOrUpdateStoreApp(config); err != nil { + s.log.Errorf("failed to update app config in store, %v", err) + return &serverpb.UpdateStoreAppRsponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to update app config in store", + }, nil + } + + s.log.Infof("Update store app [%s:%s] request successful", request.AppConfig.AppName, request.AppConfig.Version) + return &serverpb.UpdateStoreAppRsponse{ + Status: serverpb.StatusCode_OK, + StatusMessage: "app config is sucessfuly updated", + }, nil +} diff --git a/server/pkg/api/upgrade_store_app.go b/server/pkg/api/upgrade_store_app.go new file mode 100644 index 00000000..c4961a56 --- /dev/null +++ b/server/pkg/api/upgrade_store_app.go @@ -0,0 +1,102 @@ +package api + +import ( + "context" + "encoding/hex" + + "github.com/kube-tarian/kad/server/pkg/pb/agentpb" + "github.com/kube-tarian/kad/server/pkg/pb/serverpb" + "gopkg.in/yaml.v2" +) + +func (s *Server) UpgradeStoreApp(ctx context.Context, request *serverpb.UpgradeStoreAppRequest) ( + *serverpb.UpgradeStoreAppResponse, error) { + orgId, err := validateRequest(ctx, request.ClusterID, request.AppName, request.Version) + if err != nil { + s.log.Infof("request validation failed", err) + return &serverpb.UpgradeStoreAppResponse{ + Status: serverpb.StatusCode_INVALID_ARGUMENT, + StatusMessage: "request validation failed", + }, nil + } + + s.log.Infof("Upgrade store app [%s:%s] request for cluster %s recieved, [org: %s]", + request.AppName, request.Version, request.ClusterID, orgId) + + config, err := s.serverStore.GetAppFromStore(request.AppName, request.Version) + if err != nil { + s.log.Errorf("failed to get store app values, %v", err) + return &serverpb.UpgradeStoreAppResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to find store app values", + }, nil + } + + marshaledOverride, err := yaml.Marshal(config.OverrideValues) + if err != nil { + return &serverpb.UpgradeStoreAppResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to find store app values", + }, nil + } + + marshaledLaunchUi, err := yaml.Marshal(config.LaunchUIValues) + if err != nil { + return &serverpb.UpgradeStoreAppResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to find store app values", + }, nil + } + + decodedIconBytes, _ := hex.DecodeString(config.Icon) + req := &agentpb.UpgradeAppRequest{ + AppConfig: &agentpb.AppConfig{ + AppName: config.Name, + Version: config.Version, + ReleaseName: config.ReleaseName, + Category: config.Category, + Description: config.Description, + ChartName: config.ChartName, + RepoName: config.RepoName, + RepoURL: config.RepoURL, + Namespace: config.Namespace, + CreateNamespace: config.CreateNamespace, + PrivilegedNamespace: config.PrivilegedNamespace, + Icon: decodedIconBytes, + LaunchURL: config.LaunchURL, + LaunchUIDescription: config.LaunchUIDescription, + DefualtApp: false, + }, + AppValues: &agentpb.AppValues{ + OverrideValues: request.OverrideValues, + LaunchUIValues: decodeBase64StringToBytes(string(marshaledLaunchUi)), + TemplateValues: decodeBase64StringToBytes(string(marshaledOverride)), + }, + } + + agent, err := s.agentHandeler.GetAgent(orgId, request.ClusterID) + if err != nil { + s.log.Errorf("failed to initialize agent, %v", err) + return &serverpb.UpgradeStoreAppResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to deploy the app", + }, nil + } + + _, err = agent.GetClient().UpgradeApp(ctx, req) + if err != nil { + s.log.Errorf("failed to deploy app, %v", err) + return &serverpb.UpgradeStoreAppResponse{ + Status: serverpb.StatusCode_INTERNRAL_ERROR, + StatusMessage: "failed to deploy the app", + }, nil + } + + s.log.Infof("Upgrade store app [%s:%s] request request triggered for cluster %s, [org: %s]", + request.AppName, request.Version, request.ClusterID, orgId) + + return &serverpb.UpgradeStoreAppResponse{ + Status: serverpb.StatusCode_OK, + StatusMessage: "app is successfully deployed", + }, nil +} diff --git a/server/pkg/api/validate_request.go b/server/pkg/api/validate_request.go new file mode 100644 index 00000000..2335ed09 --- /dev/null +++ b/server/pkg/api/validate_request.go @@ -0,0 +1,21 @@ +package api + +import ( + "context" + "fmt" +) + +func validateRequest(ctx context.Context, args ...string) (string, error) { + metadataMap := metadataContextToMap(ctx) + orgId := metadataMap[organizationIDAttribute] + if orgId == "" { + return orgId, fmt.Errorf("organizationid is missing") + } + + for _, arg := range args { + if len(arg) == 0 { + return orgId, fmt.Errorf("mandatory argument is empty") + } + } + return orgId, nil +}