diff --git a/api/docs/docs.go b/api/docs/docs.go index 44bcbc1ce0..a89077c9f8 100644 --- a/api/docs/docs.go +++ b/api/docs/docs.go @@ -6,7 +6,7 @@ import "github.com/swaggo/swag/v2" const docTemplate = `{ "schemes": {{ marshal .Schemes }}, - "components": {"schemas":{"github_com_replicatedhq_embedded-cluster_api_types.Health":{"properties":{"status":{"type":"string"}},"type":"object"},"github_com_replicatedhq_kotskinds_multitype.BoolOrString":{"type":"object"},"types.APIError":{"properties":{"errors":{"items":{"$ref":"#/components/schemas/types.APIError"},"type":"array","uniqueItems":false},"field":{"type":"string"},"message":{"type":"string"},"status_code":{"type":"integer"}},"type":"object"},"types.AppConfig":{"properties":{"groups":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigGroup"},"type":"array","uniqueItems":false}},"type":"object"},"types.AppConfigValue":{"properties":{"filename":{"type":"string"},"value":{"type":"string"}},"type":"object"},"types.AppConfigValues":{"additionalProperties":{"$ref":"#/components/schemas/types.AppConfigValue"},"type":"object"},"types.AppConfigValuesResponse":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"type":"object"},"types.AppInstall":{"properties":{"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"type":"object"},"types.AuthRequest":{"properties":{"password":{"type":"string"}},"type":"object"},"types.AuthResponse":{"properties":{"token":{"type":"string"}},"type":"object"},"types.GetListAvailableNetworkInterfacesResponse":{"properties":{"networkInterfaces":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"type":"object"},"types.Infra":{"properties":{"components":{"items":{"$ref":"#/components/schemas/types.InfraComponent"},"type":"array","uniqueItems":false},"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"type":"object"},"types.InfraComponent":{"properties":{"name":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"type":"object"},"types.InstallAppPreflightsStatusResponse":{"properties":{"allowIgnoreAppPreflights":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"type":"object"},"types.InstallAppRequest":{"properties":{"ignoreAppPreflights":{"type":"boolean"}},"type":"object"},"types.InstallHostPreflightsStatusResponse":{"properties":{"allowIgnoreHostPreflights":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"type":"object"},"types.KubernetesInstallationConfig":{"properties":{"adminConsolePort":{"type":"integer"},"httpProxy":{"type":"string"},"httpsProxy":{"type":"string"},"noProxy":{"type":"string"}},"type":"object"},"types.LinuxInfraSetupRequest":{"properties":{"ignoreHostPreflights":{"type":"boolean"}},"type":"object"},"types.LinuxInstallationConfig":{"properties":{"adminConsolePort":{"type":"integer"},"dataDirectory":{"type":"string"},"globalCidr":{"type":"string"},"httpProxy":{"type":"string"},"httpsProxy":{"type":"string"},"localArtifactMirrorPort":{"type":"integer"},"networkInterface":{"type":"string"},"noProxy":{"type":"string"},"podCidr":{"type":"string"},"serviceCidr":{"type":"string"}},"type":"object"},"types.PatchAppConfigValuesRequest":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"type":"object"},"types.PostInstallRunHostPreflightsRequest":{"properties":{"isUi":{"type":"boolean"}},"type":"object"},"types.PreflightsOutput":{"properties":{"fail":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false},"pass":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false},"warn":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false}},"type":"object"},"types.PreflightsRecord":{"properties":{"message":{"type":"string"},"title":{"type":"string"}},"type":"object"},"types.State":{"enum":["Pending","Running","Succeeded","Failed"],"example":"Succeeded","type":"string","x-enum-varnames":["StatePending","StateRunning","StateSucceeded","StateFailed"]},"types.Status":{"properties":{"description":{"type":"string"},"lastUpdated":{"type":"string"},"state":{"$ref":"#/components/schemas/types.State"}},"type":"object"},"types.TemplateAppConfigRequest":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"type":"object"},"v1beta1.ConfigChildItem":{"properties":{"default":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"name":{"type":"string"},"recommended":{"type":"boolean"},"title":{"type":"string"},"value":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"}},"type":"object"},"v1beta1.ConfigGroup":{"properties":{"description":{"type":"string"},"items":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigItem"},"type":"array","uniqueItems":false},"name":{"type":"string"},"title":{"type":"string"},"when":{"type":"string"}},"type":"object"},"v1beta1.ConfigItem":{"properties":{"affix":{"type":"string"},"countByGroup":{"additionalProperties":{"type":"integer"},"type":"object"},"data":{"type":"string"},"default":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"error":{"type":"string"},"filename":{"type":"string"},"help_text":{"type":"string"},"hidden":{"type":"boolean"},"items":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigChildItem"},"type":"array","uniqueItems":false},"minimumCount":{"type":"integer"},"multi_value":{"items":{"type":"string"},"type":"array","uniqueItems":false},"multiple":{"type":"boolean"},"name":{"type":"string"},"readonly":{"type":"boolean"},"recommended":{"type":"boolean"},"repeatable":{"type":"boolean"},"required":{"type":"boolean"},"templates":{"items":{"$ref":"#/components/schemas/v1beta1.RepeatTemplate"},"type":"array","uniqueItems":false},"title":{"type":"string"},"type":{"type":"string"},"validation":{"$ref":"#/components/schemas/v1beta1.ConfigItemValidation"},"value":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"valuesByGroup":{"$ref":"#/components/schemas/v1beta1.ValuesByGroup"},"when":{"type":"string"},"write_once":{"type":"boolean"}},"type":"object"},"v1beta1.ConfigItemValidation":{"properties":{"regex":{"$ref":"#/components/schemas/v1beta1.RegexValidator"}},"type":"object"},"v1beta1.GroupValues":{"additionalProperties":{"type":"string"},"type":"object"},"v1beta1.RegexValidator":{"properties":{"message":{"type":"string"},"pattern":{"type":"string"}},"type":"object"},"v1beta1.RepeatTemplate":{"properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"name":{"type":"string"},"namespace":{"type":"string"},"yamlPath":{"type":"string"}},"type":"object"},"v1beta1.ValuesByGroup":{"additionalProperties":{"$ref":"#/components/schemas/v1beta1.GroupValues"},"type":"object"}},"securitySchemes":{"bearerauth":{"bearerFormat":"JWT","scheme":"bearer","type":"http"}}}, + "components": {"schemas":{"github_com_replicatedhq_embedded-cluster_api_types.Health":{"properties":{"status":{"type":"string"}},"type":"object"},"github_com_replicatedhq_kotskinds_multitype.BoolOrString":{"type":"object"},"types.APIError":{"properties":{"errors":{"items":{"$ref":"#/components/schemas/types.APIError"},"type":"array","uniqueItems":false},"field":{"type":"string"},"message":{"type":"string"},"status_code":{"type":"integer"}},"type":"object"},"types.AppComponent":{"properties":{"name":{"description":"Chart name","type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"type":"object"},"types.AppConfig":{"properties":{"groups":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigGroup"},"type":"array","uniqueItems":false}},"type":"object"},"types.AppConfigValue":{"properties":{"filename":{"type":"string"},"value":{"type":"string"}},"type":"object"},"types.AppConfigValues":{"additionalProperties":{"$ref":"#/components/schemas/types.AppConfigValue"},"type":"object"},"types.AppConfigValuesResponse":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"type":"object"},"types.AppInstall":{"properties":{"components":{"items":{"$ref":"#/components/schemas/types.AppComponent"},"type":"array","uniqueItems":false},"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"type":"object"},"types.AuthRequest":{"properties":{"password":{"type":"string"}},"type":"object"},"types.AuthResponse":{"properties":{"token":{"type":"string"}},"type":"object"},"types.GetListAvailableNetworkInterfacesResponse":{"properties":{"networkInterfaces":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"type":"object"},"types.Infra":{"properties":{"components":{"items":{"$ref":"#/components/schemas/types.InfraComponent"},"type":"array","uniqueItems":false},"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"type":"object"},"types.InfraComponent":{"properties":{"name":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"type":"object"},"types.InstallAppPreflightsStatusResponse":{"properties":{"allowIgnoreAppPreflights":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"type":"object"},"types.InstallAppRequest":{"properties":{"ignoreAppPreflights":{"type":"boolean"}},"type":"object"},"types.InstallHostPreflightsStatusResponse":{"properties":{"allowIgnoreHostPreflights":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"type":"object"},"types.KubernetesInstallationConfig":{"properties":{"adminConsolePort":{"type":"integer"},"httpProxy":{"type":"string"},"httpsProxy":{"type":"string"},"noProxy":{"type":"string"}},"type":"object"},"types.LinuxInfraSetupRequest":{"properties":{"ignoreHostPreflights":{"type":"boolean"}},"type":"object"},"types.LinuxInstallationConfig":{"properties":{"adminConsolePort":{"type":"integer"},"dataDirectory":{"type":"string"},"globalCidr":{"type":"string"},"httpProxy":{"type":"string"},"httpsProxy":{"type":"string"},"localArtifactMirrorPort":{"type":"integer"},"networkInterface":{"type":"string"},"noProxy":{"type":"string"},"podCidr":{"type":"string"},"serviceCidr":{"type":"string"}},"type":"object"},"types.PatchAppConfigValuesRequest":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"type":"object"},"types.PostInstallRunHostPreflightsRequest":{"properties":{"isUi":{"type":"boolean"}},"type":"object"},"types.PreflightsOutput":{"properties":{"fail":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false},"pass":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false},"warn":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false}},"type":"object"},"types.PreflightsRecord":{"properties":{"message":{"type":"string"},"title":{"type":"string"}},"type":"object"},"types.State":{"enum":["Pending","Running","Succeeded","Failed"],"example":"Succeeded","type":"string","x-enum-varnames":["StatePending","StateRunning","StateSucceeded","StateFailed"]},"types.Status":{"description":"Uses existing Status type","properties":{"description":{"type":"string"},"lastUpdated":{"type":"string"},"state":{"$ref":"#/components/schemas/types.State"}},"type":"object"},"types.TemplateAppConfigRequest":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"type":"object"},"v1beta1.ConfigChildItem":{"properties":{"default":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"name":{"type":"string"},"recommended":{"type":"boolean"},"title":{"type":"string"},"value":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"}},"type":"object"},"v1beta1.ConfigGroup":{"properties":{"description":{"type":"string"},"items":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigItem"},"type":"array","uniqueItems":false},"name":{"type":"string"},"title":{"type":"string"},"when":{"type":"string"}},"type":"object"},"v1beta1.ConfigItem":{"properties":{"affix":{"type":"string"},"countByGroup":{"additionalProperties":{"type":"integer"},"type":"object"},"data":{"type":"string"},"default":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"error":{"type":"string"},"filename":{"type":"string"},"help_text":{"type":"string"},"hidden":{"type":"boolean"},"items":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigChildItem"},"type":"array","uniqueItems":false},"minimumCount":{"type":"integer"},"multi_value":{"items":{"type":"string"},"type":"array","uniqueItems":false},"multiple":{"type":"boolean"},"name":{"type":"string"},"readonly":{"type":"boolean"},"recommended":{"type":"boolean"},"repeatable":{"type":"boolean"},"required":{"type":"boolean"},"templates":{"items":{"$ref":"#/components/schemas/v1beta1.RepeatTemplate"},"type":"array","uniqueItems":false},"title":{"type":"string"},"type":{"type":"string"},"validation":{"$ref":"#/components/schemas/v1beta1.ConfigItemValidation"},"value":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"valuesByGroup":{"$ref":"#/components/schemas/v1beta1.ValuesByGroup"},"when":{"type":"string"},"write_once":{"type":"boolean"}},"type":"object"},"v1beta1.ConfigItemValidation":{"properties":{"regex":{"$ref":"#/components/schemas/v1beta1.RegexValidator"}},"type":"object"},"v1beta1.GroupValues":{"additionalProperties":{"type":"string"},"type":"object"},"v1beta1.RegexValidator":{"properties":{"message":{"type":"string"},"pattern":{"type":"string"}},"type":"object"},"v1beta1.RepeatTemplate":{"properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"name":{"type":"string"},"namespace":{"type":"string"},"yamlPath":{"type":"string"}},"type":"object"},"v1beta1.ValuesByGroup":{"additionalProperties":{"$ref":"#/components/schemas/v1beta1.GroupValues"},"type":"object"}},"securitySchemes":{"bearerauth":{"bearerFormat":"JWT","scheme":"bearer","type":"http"}}}, "info": {"contact":{"email":"support@replicated.com","name":"API Support","url":"https://github.com/replicatedhq/embedded-cluster/issues"},"description":"{{escape .Description}}","license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"},"termsOfService":"http://swagger.io/terms/","title":"{{.Title}}","version":"{{.Version}}"}, "externalDocs": {"description":"OpenAPI","url":"https://swagger.io/resources/open-api/"}, "paths": {"/auth/login":{"post":{"description":"Authenticate a user","operationId":"postAuthLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AuthRequest"}}},"description":"Auth Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AuthResponse"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Unauthorized"}},"summary":"Authenticate a user","tags":["auth"]}},"/console/available-network-interfaces":{"get":{"description":"List available network interfaces","operationId":"getConsoleListAvailableNetworkInterfaces","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.GetListAvailableNetworkInterfacesResponse"}}},"description":"OK"}},"summary":"List available network interfaces","tags":["console"]}},"/health":{"get":{"description":"get the health of the API","operationId":"getHealth","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/github_com_replicatedhq_embedded-cluster_api_types.Health"}}},"description":"OK"}},"summary":"Get the health of the API","tags":["health"]}},"/kubernetes/install/app-preflights/run":{"post":{"description":"Run install app preflight checks using current app configuration","operationId":"postKubernetesInstallRunAppPreflights","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Run install app preflight checks","tags":["kubernetes-install"]}},"/kubernetes/install/app-preflights/status":{"get":{"description":"Get the current status and results of app preflight checks for install","operationId":"getKubernetesInstallAppPreflightsStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app preflight status for install","tags":["kubernetes-install"]}},"/kubernetes/install/app/config/template":{"post":{"description":"Template the app config with provided values and return the templated config","operationId":"postKubernetesInstallTemplateAppConfig","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.TemplateAppConfigRequest"}}},"description":"Template App Config Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfig"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Template the app config with provided values","tags":["kubernetes-install"]}},"/kubernetes/install/app/config/values":{"get":{"description":"Get the current app config values","operationId":"getKubernetesInstallAppConfigValues","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get the app config values","tags":["kubernetes-install"]},"patch":{"description":"Set the app config values with partial updates","operationId":"patchKubernetesInstallAppConfigValues","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.PatchAppConfigValuesRequest"}}},"description":"Patch App Config Values Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Set the app config values","tags":["kubernetes-install"]}},"/kubernetes/install/app/install":{"post":{"description":"Install the app using current configuration","operationId":"postKubernetesInstallApp","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppRequest"}}},"description":"Install App Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppInstall"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Install the app","tags":["kubernetes-install"]}},"/kubernetes/install/app/status":{"get":{"description":"Get the current status of app installation","operationId":"getKubernetesInstallAppStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppInstall"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app install status","tags":["kubernetes-install"]}},"/kubernetes/install/infra/setup":{"post":{"description":"Setup infra components","operationId":"postKubernetesInstallSetupInfra","requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Setup infra components","tags":["kubernetes-install"]}},"/kubernetes/install/infra/status":{"get":{"description":"Get the current status of the infra","operationId":"getKubernetesInstallInfraStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get the status of the infra","tags":["kubernetes-install"]}},"/kubernetes/install/installation/config":{"get":{"description":"get the Kubernetes installation config","operationId":"getKubernetesInstallInstallationConfig","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.KubernetesInstallationConfig"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get the Kubernetes installation config","tags":["kubernetes-install"]}},"/kubernetes/install/installation/configure":{"post":{"description":"configure the Kubernetes installation for install","operationId":"postKubernetesInstallConfigureInstallation","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.KubernetesInstallationConfig"}}},"description":"Installation config","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Status"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Configure the Kubernetes installation for install","tags":["kubernetes-install"]}},"/kubernetes/install/installation/status":{"get":{"description":"Get the current status of the installation configuration for install","operationId":"getKubernetesInstallInstallationStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Status"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get installation configuration status for install","tags":["kubernetes-install"]}},"/linux/install/app-preflights/run":{"post":{"description":"Run install app preflight checks using current app configuration","operationId":"postLinuxInstallRunAppPreflights","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Run install app preflight checks","tags":["linux-install"]}},"/linux/install/app-preflights/status":{"get":{"description":"Get the current status and results of app preflight checks for install","operationId":"getLinuxInstallAppPreflightsStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app preflight status for install","tags":["linux-install"]}},"/linux/install/app/config/template":{"post":{"description":"Template the app config with provided values and return the templated config","operationId":"postLinuxInstallTemplateAppConfig","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.TemplateAppConfigRequest"}}},"description":"Template App Config Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfig"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Template the app config with provided values","tags":["linux-install"]}},"/linux/install/app/config/values":{"get":{"description":"Get the current app config values","operationId":"getLinuxInstallAppConfigValues","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get the app config values","tags":["linux-install"]},"patch":{"description":"Set the app config values with partial updates","operationId":"patchLinuxInstallAppConfigValues","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.PatchAppConfigValuesRequest"}}},"description":"Patch App Config Values Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Set the app config values","tags":["linux-install"]}},"/linux/install/app/install":{"post":{"description":"Install the app using current configuration","operationId":"postLinuxInstallApp","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppRequest"}}},"description":"Install App Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppInstall"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Install the app","tags":["linux-install"]}},"/linux/install/app/status":{"get":{"description":"Get the current status of app installation","operationId":"getLinuxInstallAppStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppInstall"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app install status","tags":["linux-install"]}},"/linux/install/host-preflights/run":{"post":{"description":"Run install host preflight checks using installation config and client-provided data","operationId":"postLinuxInstallRunHostPreflights","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.PostInstallRunHostPreflightsRequest"}}},"description":"Post Install Run Host Preflights Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallHostPreflightsStatusResponse"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Run install host preflight checks","tags":["linux-install"]}},"/linux/install/host-preflights/status":{"get":{"description":"Get the current status and results of host preflight checks for install","operationId":"getLinuxInstallHostPreflightsStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallHostPreflightsStatusResponse"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get host preflight status for install","tags":["linux-install"]}},"/linux/install/infra/setup":{"post":{"description":"Setup infra components","operationId":"postLinuxInstallSetupInfra","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.LinuxInfraSetupRequest"}}},"description":"Infra Setup Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Setup infra components","tags":["linux-install"]}},"/linux/install/infra/status":{"get":{"description":"Get the current status of the infra","operationId":"getLinuxInstallInfraStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get the status of the infra","tags":["linux-install"]}},"/linux/install/installation/config":{"get":{"description":"get the installation config","operationId":"getLinuxInstallInstallationConfig","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get the installation config","tags":["linux-install"]}},"/linux/install/installation/configure":{"post":{"description":"configure the installation for install","operationId":"postLinuxInstallConfigureInstallation","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"}}},"description":"Installation config","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Status"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Configure the installation for install","tags":["linux-install"]}},"/linux/install/installation/status":{"get":{"description":"Get the current status of the installation configuration for install","operationId":"getLinuxInstallInstallationStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Status"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get installation configuration status for install","tags":["linux-install"]}}}, diff --git a/api/docs/swagger.json b/api/docs/swagger.json index 3a0eb7729d..15ac1e2849 100644 --- a/api/docs/swagger.json +++ b/api/docs/swagger.json @@ -1,5 +1,5 @@ { - "components": {"schemas":{"github_com_replicatedhq_embedded-cluster_api_types.Health":{"properties":{"status":{"type":"string"}},"type":"object"},"github_com_replicatedhq_kotskinds_multitype.BoolOrString":{"type":"object"},"types.APIError":{"properties":{"errors":{"items":{"$ref":"#/components/schemas/types.APIError"},"type":"array","uniqueItems":false},"field":{"type":"string"},"message":{"type":"string"},"status_code":{"type":"integer"}},"type":"object"},"types.AppConfig":{"properties":{"groups":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigGroup"},"type":"array","uniqueItems":false}},"type":"object"},"types.AppConfigValue":{"properties":{"filename":{"type":"string"},"value":{"type":"string"}},"type":"object"},"types.AppConfigValues":{"additionalProperties":{"$ref":"#/components/schemas/types.AppConfigValue"},"type":"object"},"types.AppConfigValuesResponse":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"type":"object"},"types.AppInstall":{"properties":{"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"type":"object"},"types.AuthRequest":{"properties":{"password":{"type":"string"}},"type":"object"},"types.AuthResponse":{"properties":{"token":{"type":"string"}},"type":"object"},"types.GetListAvailableNetworkInterfacesResponse":{"properties":{"networkInterfaces":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"type":"object"},"types.Infra":{"properties":{"components":{"items":{"$ref":"#/components/schemas/types.InfraComponent"},"type":"array","uniqueItems":false},"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"type":"object"},"types.InfraComponent":{"properties":{"name":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"type":"object"},"types.InstallAppPreflightsStatusResponse":{"properties":{"allowIgnoreAppPreflights":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"type":"object"},"types.InstallAppRequest":{"properties":{"ignoreAppPreflights":{"type":"boolean"}},"type":"object"},"types.InstallHostPreflightsStatusResponse":{"properties":{"allowIgnoreHostPreflights":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"type":"object"},"types.KubernetesInstallationConfig":{"properties":{"adminConsolePort":{"type":"integer"},"httpProxy":{"type":"string"},"httpsProxy":{"type":"string"},"noProxy":{"type":"string"}},"type":"object"},"types.LinuxInfraSetupRequest":{"properties":{"ignoreHostPreflights":{"type":"boolean"}},"type":"object"},"types.LinuxInstallationConfig":{"properties":{"adminConsolePort":{"type":"integer"},"dataDirectory":{"type":"string"},"globalCidr":{"type":"string"},"httpProxy":{"type":"string"},"httpsProxy":{"type":"string"},"localArtifactMirrorPort":{"type":"integer"},"networkInterface":{"type":"string"},"noProxy":{"type":"string"},"podCidr":{"type":"string"},"serviceCidr":{"type":"string"}},"type":"object"},"types.PatchAppConfigValuesRequest":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"type":"object"},"types.PostInstallRunHostPreflightsRequest":{"properties":{"isUi":{"type":"boolean"}},"type":"object"},"types.PreflightsOutput":{"properties":{"fail":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false},"pass":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false},"warn":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false}},"type":"object"},"types.PreflightsRecord":{"properties":{"message":{"type":"string"},"title":{"type":"string"}},"type":"object"},"types.State":{"enum":["Pending","Running","Succeeded","Failed"],"example":"Succeeded","type":"string","x-enum-varnames":["StatePending","StateRunning","StateSucceeded","StateFailed"]},"types.Status":{"properties":{"description":{"type":"string"},"lastUpdated":{"type":"string"},"state":{"$ref":"#/components/schemas/types.State"}},"type":"object"},"types.TemplateAppConfigRequest":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"type":"object"},"v1beta1.ConfigChildItem":{"properties":{"default":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"name":{"type":"string"},"recommended":{"type":"boolean"},"title":{"type":"string"},"value":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"}},"type":"object"},"v1beta1.ConfigGroup":{"properties":{"description":{"type":"string"},"items":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigItem"},"type":"array","uniqueItems":false},"name":{"type":"string"},"title":{"type":"string"},"when":{"type":"string"}},"type":"object"},"v1beta1.ConfigItem":{"properties":{"affix":{"type":"string"},"countByGroup":{"additionalProperties":{"type":"integer"},"type":"object"},"data":{"type":"string"},"default":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"error":{"type":"string"},"filename":{"type":"string"},"help_text":{"type":"string"},"hidden":{"type":"boolean"},"items":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigChildItem"},"type":"array","uniqueItems":false},"minimumCount":{"type":"integer"},"multi_value":{"items":{"type":"string"},"type":"array","uniqueItems":false},"multiple":{"type":"boolean"},"name":{"type":"string"},"readonly":{"type":"boolean"},"recommended":{"type":"boolean"},"repeatable":{"type":"boolean"},"required":{"type":"boolean"},"templates":{"items":{"$ref":"#/components/schemas/v1beta1.RepeatTemplate"},"type":"array","uniqueItems":false},"title":{"type":"string"},"type":{"type":"string"},"validation":{"$ref":"#/components/schemas/v1beta1.ConfigItemValidation"},"value":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"valuesByGroup":{"$ref":"#/components/schemas/v1beta1.ValuesByGroup"},"when":{"type":"string"},"write_once":{"type":"boolean"}},"type":"object"},"v1beta1.ConfigItemValidation":{"properties":{"regex":{"$ref":"#/components/schemas/v1beta1.RegexValidator"}},"type":"object"},"v1beta1.GroupValues":{"additionalProperties":{"type":"string"},"type":"object"},"v1beta1.RegexValidator":{"properties":{"message":{"type":"string"},"pattern":{"type":"string"}},"type":"object"},"v1beta1.RepeatTemplate":{"properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"name":{"type":"string"},"namespace":{"type":"string"},"yamlPath":{"type":"string"}},"type":"object"},"v1beta1.ValuesByGroup":{"additionalProperties":{"$ref":"#/components/schemas/v1beta1.GroupValues"},"type":"object"}},"securitySchemes":{"bearerauth":{"bearerFormat":"JWT","scheme":"bearer","type":"http"}}}, + "components": {"schemas":{"github_com_replicatedhq_embedded-cluster_api_types.Health":{"properties":{"status":{"type":"string"}},"type":"object"},"github_com_replicatedhq_kotskinds_multitype.BoolOrString":{"type":"object"},"types.APIError":{"properties":{"errors":{"items":{"$ref":"#/components/schemas/types.APIError"},"type":"array","uniqueItems":false},"field":{"type":"string"},"message":{"type":"string"},"status_code":{"type":"integer"}},"type":"object"},"types.AppComponent":{"properties":{"name":{"description":"Chart name","type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"type":"object"},"types.AppConfig":{"properties":{"groups":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigGroup"},"type":"array","uniqueItems":false}},"type":"object"},"types.AppConfigValue":{"properties":{"filename":{"type":"string"},"value":{"type":"string"}},"type":"object"},"types.AppConfigValues":{"additionalProperties":{"$ref":"#/components/schemas/types.AppConfigValue"},"type":"object"},"types.AppConfigValuesResponse":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"type":"object"},"types.AppInstall":{"properties":{"components":{"items":{"$ref":"#/components/schemas/types.AppComponent"},"type":"array","uniqueItems":false},"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"type":"object"},"types.AuthRequest":{"properties":{"password":{"type":"string"}},"type":"object"},"types.AuthResponse":{"properties":{"token":{"type":"string"}},"type":"object"},"types.GetListAvailableNetworkInterfacesResponse":{"properties":{"networkInterfaces":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"type":"object"},"types.Infra":{"properties":{"components":{"items":{"$ref":"#/components/schemas/types.InfraComponent"},"type":"array","uniqueItems":false},"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"type":"object"},"types.InfraComponent":{"properties":{"name":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"type":"object"},"types.InstallAppPreflightsStatusResponse":{"properties":{"allowIgnoreAppPreflights":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"type":"object"},"types.InstallAppRequest":{"properties":{"ignoreAppPreflights":{"type":"boolean"}},"type":"object"},"types.InstallHostPreflightsStatusResponse":{"properties":{"allowIgnoreHostPreflights":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"type":"object"},"types.KubernetesInstallationConfig":{"properties":{"adminConsolePort":{"type":"integer"},"httpProxy":{"type":"string"},"httpsProxy":{"type":"string"},"noProxy":{"type":"string"}},"type":"object"},"types.LinuxInfraSetupRequest":{"properties":{"ignoreHostPreflights":{"type":"boolean"}},"type":"object"},"types.LinuxInstallationConfig":{"properties":{"adminConsolePort":{"type":"integer"},"dataDirectory":{"type":"string"},"globalCidr":{"type":"string"},"httpProxy":{"type":"string"},"httpsProxy":{"type":"string"},"localArtifactMirrorPort":{"type":"integer"},"networkInterface":{"type":"string"},"noProxy":{"type":"string"},"podCidr":{"type":"string"},"serviceCidr":{"type":"string"}},"type":"object"},"types.PatchAppConfigValuesRequest":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"type":"object"},"types.PostInstallRunHostPreflightsRequest":{"properties":{"isUi":{"type":"boolean"}},"type":"object"},"types.PreflightsOutput":{"properties":{"fail":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false},"pass":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false},"warn":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false}},"type":"object"},"types.PreflightsRecord":{"properties":{"message":{"type":"string"},"title":{"type":"string"}},"type":"object"},"types.State":{"enum":["Pending","Running","Succeeded","Failed"],"example":"Succeeded","type":"string","x-enum-varnames":["StatePending","StateRunning","StateSucceeded","StateFailed"]},"types.Status":{"description":"Uses existing Status type","properties":{"description":{"type":"string"},"lastUpdated":{"type":"string"},"state":{"$ref":"#/components/schemas/types.State"}},"type":"object"},"types.TemplateAppConfigRequest":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"type":"object"},"v1beta1.ConfigChildItem":{"properties":{"default":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"name":{"type":"string"},"recommended":{"type":"boolean"},"title":{"type":"string"},"value":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"}},"type":"object"},"v1beta1.ConfigGroup":{"properties":{"description":{"type":"string"},"items":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigItem"},"type":"array","uniqueItems":false},"name":{"type":"string"},"title":{"type":"string"},"when":{"type":"string"}},"type":"object"},"v1beta1.ConfigItem":{"properties":{"affix":{"type":"string"},"countByGroup":{"additionalProperties":{"type":"integer"},"type":"object"},"data":{"type":"string"},"default":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"error":{"type":"string"},"filename":{"type":"string"},"help_text":{"type":"string"},"hidden":{"type":"boolean"},"items":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigChildItem"},"type":"array","uniqueItems":false},"minimumCount":{"type":"integer"},"multi_value":{"items":{"type":"string"},"type":"array","uniqueItems":false},"multiple":{"type":"boolean"},"name":{"type":"string"},"readonly":{"type":"boolean"},"recommended":{"type":"boolean"},"repeatable":{"type":"boolean"},"required":{"type":"boolean"},"templates":{"items":{"$ref":"#/components/schemas/v1beta1.RepeatTemplate"},"type":"array","uniqueItems":false},"title":{"type":"string"},"type":{"type":"string"},"validation":{"$ref":"#/components/schemas/v1beta1.ConfigItemValidation"},"value":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"valuesByGroup":{"$ref":"#/components/schemas/v1beta1.ValuesByGroup"},"when":{"type":"string"},"write_once":{"type":"boolean"}},"type":"object"},"v1beta1.ConfigItemValidation":{"properties":{"regex":{"$ref":"#/components/schemas/v1beta1.RegexValidator"}},"type":"object"},"v1beta1.GroupValues":{"additionalProperties":{"type":"string"},"type":"object"},"v1beta1.RegexValidator":{"properties":{"message":{"type":"string"},"pattern":{"type":"string"}},"type":"object"},"v1beta1.RepeatTemplate":{"properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"name":{"type":"string"},"namespace":{"type":"string"},"yamlPath":{"type":"string"}},"type":"object"},"v1beta1.ValuesByGroup":{"additionalProperties":{"$ref":"#/components/schemas/v1beta1.GroupValues"},"type":"object"}},"securitySchemes":{"bearerauth":{"bearerFormat":"JWT","scheme":"bearer","type":"http"}}}, "info": {"contact":{"email":"support@replicated.com","name":"API Support","url":"https://github.com/replicatedhq/embedded-cluster/issues"},"description":"This is the API for the Embedded Cluster project.","license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"},"termsOfService":"http://swagger.io/terms/","title":"Embedded Cluster API","version":"0.1"}, "externalDocs": {"description":"OpenAPI","url":"https://swagger.io/resources/open-api/"}, "paths": {"/auth/login":{"post":{"description":"Authenticate a user","operationId":"postAuthLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AuthRequest"}}},"description":"Auth Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AuthResponse"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Unauthorized"}},"summary":"Authenticate a user","tags":["auth"]}},"/console/available-network-interfaces":{"get":{"description":"List available network interfaces","operationId":"getConsoleListAvailableNetworkInterfaces","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.GetListAvailableNetworkInterfacesResponse"}}},"description":"OK"}},"summary":"List available network interfaces","tags":["console"]}},"/health":{"get":{"description":"get the health of the API","operationId":"getHealth","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/github_com_replicatedhq_embedded-cluster_api_types.Health"}}},"description":"OK"}},"summary":"Get the health of the API","tags":["health"]}},"/kubernetes/install/app-preflights/run":{"post":{"description":"Run install app preflight checks using current app configuration","operationId":"postKubernetesInstallRunAppPreflights","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Run install app preflight checks","tags":["kubernetes-install"]}},"/kubernetes/install/app-preflights/status":{"get":{"description":"Get the current status and results of app preflight checks for install","operationId":"getKubernetesInstallAppPreflightsStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app preflight status for install","tags":["kubernetes-install"]}},"/kubernetes/install/app/config/template":{"post":{"description":"Template the app config with provided values and return the templated config","operationId":"postKubernetesInstallTemplateAppConfig","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.TemplateAppConfigRequest"}}},"description":"Template App Config Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfig"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Template the app config with provided values","tags":["kubernetes-install"]}},"/kubernetes/install/app/config/values":{"get":{"description":"Get the current app config values","operationId":"getKubernetesInstallAppConfigValues","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get the app config values","tags":["kubernetes-install"]},"patch":{"description":"Set the app config values with partial updates","operationId":"patchKubernetesInstallAppConfigValues","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.PatchAppConfigValuesRequest"}}},"description":"Patch App Config Values Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Set the app config values","tags":["kubernetes-install"]}},"/kubernetes/install/app/install":{"post":{"description":"Install the app using current configuration","operationId":"postKubernetesInstallApp","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppRequest"}}},"description":"Install App Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppInstall"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Install the app","tags":["kubernetes-install"]}},"/kubernetes/install/app/status":{"get":{"description":"Get the current status of app installation","operationId":"getKubernetesInstallAppStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppInstall"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app install status","tags":["kubernetes-install"]}},"/kubernetes/install/infra/setup":{"post":{"description":"Setup infra components","operationId":"postKubernetesInstallSetupInfra","requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Setup infra components","tags":["kubernetes-install"]}},"/kubernetes/install/infra/status":{"get":{"description":"Get the current status of the infra","operationId":"getKubernetesInstallInfraStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get the status of the infra","tags":["kubernetes-install"]}},"/kubernetes/install/installation/config":{"get":{"description":"get the Kubernetes installation config","operationId":"getKubernetesInstallInstallationConfig","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.KubernetesInstallationConfig"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get the Kubernetes installation config","tags":["kubernetes-install"]}},"/kubernetes/install/installation/configure":{"post":{"description":"configure the Kubernetes installation for install","operationId":"postKubernetesInstallConfigureInstallation","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.KubernetesInstallationConfig"}}},"description":"Installation config","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Status"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Configure the Kubernetes installation for install","tags":["kubernetes-install"]}},"/kubernetes/install/installation/status":{"get":{"description":"Get the current status of the installation configuration for install","operationId":"getKubernetesInstallInstallationStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Status"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get installation configuration status for install","tags":["kubernetes-install"]}},"/linux/install/app-preflights/run":{"post":{"description":"Run install app preflight checks using current app configuration","operationId":"postLinuxInstallRunAppPreflights","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Run install app preflight checks","tags":["linux-install"]}},"/linux/install/app-preflights/status":{"get":{"description":"Get the current status and results of app preflight checks for install","operationId":"getLinuxInstallAppPreflightsStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app preflight status for install","tags":["linux-install"]}},"/linux/install/app/config/template":{"post":{"description":"Template the app config with provided values and return the templated config","operationId":"postLinuxInstallTemplateAppConfig","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.TemplateAppConfigRequest"}}},"description":"Template App Config Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfig"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Template the app config with provided values","tags":["linux-install"]}},"/linux/install/app/config/values":{"get":{"description":"Get the current app config values","operationId":"getLinuxInstallAppConfigValues","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get the app config values","tags":["linux-install"]},"patch":{"description":"Set the app config values with partial updates","operationId":"patchLinuxInstallAppConfigValues","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.PatchAppConfigValuesRequest"}}},"description":"Patch App Config Values Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Set the app config values","tags":["linux-install"]}},"/linux/install/app/install":{"post":{"description":"Install the app using current configuration","operationId":"postLinuxInstallApp","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppRequest"}}},"description":"Install App Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppInstall"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Install the app","tags":["linux-install"]}},"/linux/install/app/status":{"get":{"description":"Get the current status of app installation","operationId":"getLinuxInstallAppStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppInstall"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app install status","tags":["linux-install"]}},"/linux/install/host-preflights/run":{"post":{"description":"Run install host preflight checks using installation config and client-provided data","operationId":"postLinuxInstallRunHostPreflights","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.PostInstallRunHostPreflightsRequest"}}},"description":"Post Install Run Host Preflights Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallHostPreflightsStatusResponse"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Run install host preflight checks","tags":["linux-install"]}},"/linux/install/host-preflights/status":{"get":{"description":"Get the current status and results of host preflight checks for install","operationId":"getLinuxInstallHostPreflightsStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallHostPreflightsStatusResponse"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get host preflight status for install","tags":["linux-install"]}},"/linux/install/infra/setup":{"post":{"description":"Setup infra components","operationId":"postLinuxInstallSetupInfra","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.LinuxInfraSetupRequest"}}},"description":"Infra Setup Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Setup infra components","tags":["linux-install"]}},"/linux/install/infra/status":{"get":{"description":"Get the current status of the infra","operationId":"getLinuxInstallInfraStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get the status of the infra","tags":["linux-install"]}},"/linux/install/installation/config":{"get":{"description":"get the installation config","operationId":"getLinuxInstallInstallationConfig","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get the installation config","tags":["linux-install"]}},"/linux/install/installation/configure":{"post":{"description":"configure the installation for install","operationId":"postLinuxInstallConfigureInstallation","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"}}},"description":"Installation config","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Status"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Configure the installation for install","tags":["linux-install"]}},"/linux/install/installation/status":{"get":{"description":"Get the current status of the installation configuration for install","operationId":"getLinuxInstallInstallationStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Status"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get installation configuration status for install","tags":["linux-install"]}}}, diff --git a/api/docs/swagger.yaml b/api/docs/swagger.yaml index 2976e7362e..f3552bf0ec 100644 --- a/api/docs/swagger.yaml +++ b/api/docs/swagger.yaml @@ -21,6 +21,14 @@ components: status_code: type: integer type: object + types.AppComponent: + properties: + name: + description: Chart name + type: string + status: + $ref: '#/components/schemas/types.Status' + type: object types.AppConfig: properties: groups: @@ -47,6 +55,11 @@ components: type: object types.AppInstall: properties: + components: + items: + $ref: '#/components/schemas/types.AppComponent' + type: array + uniqueItems: false logs: type: string status: @@ -210,6 +223,7 @@ components: - StateSucceeded - StateFailed types.Status: + description: Uses existing Status type properties: description: type: string diff --git a/api/integration/kubernetes/install/appinstall_test.go b/api/integration/kubernetes/install/appinstall_test.go index 42cf8c728e..d74bd7b2bf 100644 --- a/api/integration/kubernetes/install/appinstall_test.go +++ b/api/integration/kubernetes/install/appinstall_test.go @@ -36,11 +36,30 @@ import ( // TestGetAppInstallStatus tests the GET /kubernetes/install/app/status endpoint func TestGetAppInstallStatus(t *testing.T) { t.Run("Success", func(t *testing.T) { - // Create app install status + // Create app install status with components appInstallStatus := types.AppInstall{ + Components: []types.AppComponent{ + { + Name: "nginx-chart", + Status: types.Status{ + State: types.StateSucceeded, + Description: "Installation complete", + LastUpdated: time.Now(), + }, + }, + { + Name: "postgres-chart", + Status: types.Status{ + State: types.StateRunning, + Description: "Installing chart", + LastUpdated: time.Now(), + }, + }, + }, Status: types.Status{ State: types.StateRunning, Description: "Installing application", + LastUpdated: time.Now(), }, Logs: "Installation in progress...", } @@ -110,10 +129,25 @@ func TestGetAppInstallStatus(t *testing.T) { err = json.NewDecoder(rec.Body).Decode(&response) require.NoError(t, err) - // Verify the response + // Verify the response structure includes components assert.Equal(t, appInstallStatus.Status.State, response.Status.State) assert.Equal(t, appInstallStatus.Status.Description, response.Status.Description) assert.Equal(t, appInstallStatus.Logs, response.Logs) + + // Verify components array is present and has expected data + assert.Len(t, response.Components, 2, "Should have 2 components") + + // Verify first component (nginx-chart) + nginxComponent := response.Components[0] + assert.Equal(t, "nginx-chart", nginxComponent.Name) + assert.Equal(t, types.StateSucceeded, nginxComponent.Status.State) + assert.Equal(t, "Installation complete", nginxComponent.Status.Description) + + // Verify second component (postgres-chart) + postgresComponent := response.Components[1] + assert.Equal(t, "postgres-chart", postgresComponent.Name) + assert.Equal(t, types.StateRunning, postgresComponent.Status.State) + assert.Equal(t, "Installing chart", postgresComponent.Status.Description) }) t.Run("Authorization error", func(t *testing.T) { diff --git a/api/integration/linux/install/appinstall_test.go b/api/integration/linux/install/appinstall_test.go index 8f47dcb640..2ec7c3e465 100644 --- a/api/integration/linux/install/appinstall_test.go +++ b/api/integration/linux/install/appinstall_test.go @@ -53,11 +53,30 @@ func TestGetAppInstallStatus(t *testing.T) { } t.Run("Success", func(t *testing.T) { - // Create app install status + // Create app install status with components appInstallStatus := types.AppInstall{ + Components: []types.AppComponent{ + { + Name: "nginx-chart", + Status: types.Status{ + State: types.StateSucceeded, + Description: "Installation complete", + LastUpdated: time.Now(), + }, + }, + { + Name: "postgres-chart", + Status: types.Status{ + State: types.StateRunning, + Description: "Installing chart", + LastUpdated: time.Now(), + }, + }, + }, Status: types.Status{ State: types.StateRunning, Description: "Installing application", + LastUpdated: time.Now(), }, Logs: "Installation in progress...", } @@ -119,10 +138,25 @@ func TestGetAppInstallStatus(t *testing.T) { err = json.NewDecoder(rec.Body).Decode(&response) require.NoError(t, err) - // Verify the response + // Verify the response structure includes components assert.Equal(t, appInstallStatus.Status.State, response.Status.State) assert.Equal(t, appInstallStatus.Status.Description, response.Status.Description) assert.Equal(t, appInstallStatus.Logs, response.Logs) + + // Verify components array is present and has expected data + assert.Len(t, response.Components, 2, "Should have 2 components") + + // Verify first component (nginx-chart) + nginxComponent := response.Components[0] + assert.Equal(t, "nginx-chart", nginxComponent.Name) + assert.Equal(t, types.StateSucceeded, nginxComponent.Status.State) + assert.Equal(t, "Installation complete", nginxComponent.Status.Description) + + // Verify second component (postgres-chart) + postgresComponent := response.Components[1] + assert.Equal(t, "postgres-chart", postgresComponent.Name) + assert.Equal(t, types.StateRunning, postgresComponent.Status.State) + assert.Equal(t, "Installing chart", postgresComponent.Status.Description) }) t.Run("Authorization error", func(t *testing.T) { diff --git a/api/internal/managers/app/install/install.go b/api/internal/managers/app/install/install.go index b8f1309b40..acd20a852c 100644 --- a/api/internal/managers/app/install/install.go +++ b/api/internal/managers/app/install/install.go @@ -18,6 +18,10 @@ import ( // Install installs the app with the provided installable Helm charts and config values func (m *appInstallManager) Install(ctx context.Context, installableCharts []types.InstallableHelmChart, kotsConfigValues kotsv1beta1.ConfigValues) (finalErr error) { + if err := m.initializeComponents(installableCharts); err != nil { + return fmt.Errorf("initialize components: %w", err) + } + if err := m.setStatus(types.StateRunning, "Installing application"); err != nil { return fmt.Errorf("set status: %w", err) } @@ -109,7 +113,7 @@ func (m *appInstallManager) createConfigValuesFile(kotsConfigValues kotsv1beta1. } func (m *appInstallManager) installHelmCharts(ctx context.Context, installableCharts []types.InstallableHelmChart) error { - logFn := m.logFn("app-helm") + logFn := m.logFn("app") if len(installableCharts) == 0 { return fmt.Errorf("no helm charts found") @@ -118,19 +122,42 @@ func (m *appInstallManager) installHelmCharts(ctx context.Context, installableCh logFn("installing %d helm charts", len(installableCharts)) for _, installableChart := range installableCharts { - logFn("installing %s helm chart", installableChart.CR.GetChartName()) + chartName := installableChart.CR.GetChartName() + logFn("installing %s chart", chartName) if err := m.installHelmChart(ctx, installableChart); err != nil { - return fmt.Errorf("install %s helm chart: %w", installableChart.CR.GetChartName(), err) + return fmt.Errorf("install %s helm chart: %w", chartName, err) } - logFn("successfully installed %s helm chart", installableChart.CR.GetChartName()) + logFn("successfully installed %s chart", chartName) } return nil } -func (m *appInstallManager) installHelmChart(ctx context.Context, installableChart types.InstallableHelmChart) error { +func (m *appInstallManager) installHelmChart(ctx context.Context, installableChart types.InstallableHelmChart) (finalErr error) { + chartName := installableChart.CR.GetChartName() + + if err := m.setComponentStatus(chartName, types.StateRunning, "Installing"); err != nil { + return fmt.Errorf("set component status: %w", err) + } + + defer func() { + if r := recover(); r != nil { + finalErr = fmt.Errorf("recovered from panic: %v: %s", r, string(debug.Stack())) + } + + if finalErr != nil { + if err := m.setComponentStatus(chartName, types.StateFailed, finalErr.Error()); err != nil { + m.logger.WithError(err).Errorf("failed to set %s chart failed status", chartName) + } + } else { + if err := m.setComponentStatus(chartName, types.StateSucceeded, ""); err != nil { + m.logger.WithError(err).Errorf("failed to set %s chart succeeded status", chartName) + } + } + }() + // Write chart archive to temp file chartPath, err := m.writeChartArchiveToTemp(installableChart.Archive) if err != nil { @@ -157,3 +184,13 @@ func (m *appInstallManager) installHelmChart(ctx context.Context, installableCha return nil } + +// initializeComponents initializes the component tracking with chart names +func (m *appInstallManager) initializeComponents(charts []types.InstallableHelmChart) error { + chartNames := make([]string, 0, len(charts)) + for _, chart := range charts { + chartNames = append(chartNames, chart.CR.GetChartName()) + } + + return m.appInstallStore.RegisterComponents(chartNames) +} diff --git a/api/internal/managers/app/install/install_test.go b/api/internal/managers/app/install/install_test.go index 3bc473b88c..342145ffc3 100644 --- a/api/internal/managers/app/install/install_test.go +++ b/api/internal/managers/app/install/install_test.go @@ -5,6 +5,7 @@ import ( "bytes" "compress/gzip" "context" + "errors" "fmt" "os" "testing" @@ -388,6 +389,10 @@ func createTestHelmChartCR(name, releaseName, namespace string, weight int64) *k Name: name, }, Spec: kotsv1beta2.HelmChartSpec{ + Chart: kotsv1beta2.ChartIdentifier{ + Name: name, + ChartVersion: "1.0.0", + }, ReleaseName: releaseName, Namespace: namespace, Weight: weight, @@ -402,3 +407,118 @@ func createTestInstallableHelmChart(t *testing.T, chartName, chartVersion, relea CR: createTestHelmChartCR(chartName, releaseName, namespace, weight), } } + +// TestComponentStatusTracking tests that components are properly initialized and tracked +func TestComponentStatusTracking(t *testing.T) { + t.Run("Components are registered and status is tracked", func(t *testing.T) { + // Create test charts with different weights + installableCharts := []types.InstallableHelmChart{ + createTestInstallableHelmChart(t, "database-chart", "1.0.0", "postgres", "data", 10, map[string]any{"key": "value1"}), + createTestInstallableHelmChart(t, "web-chart", "2.0.0", "nginx", "web", 20, map[string]any{"key": "value2"}), + } + + // Create mock helm client + mockHelmClient := &helm.MockClient{} + + // Database chart installation (should be first due to lower weight) + mockHelmClient.On("Install", mock.Anything, mock.MatchedBy(func(opts helm.InstallOptions) bool { + return opts.ReleaseName == "postgres" && opts.Namespace == "data" + })).Return(&helmrelease.Release{Name: "postgres"}, nil).Once() + + // Web chart installation (should be second due to higher weight) + mockHelmClient.On("Install", mock.Anything, mock.MatchedBy(func(opts helm.InstallOptions) bool { + return opts.ReleaseName == "nginx" && opts.Namespace == "web" + })).Return(&helmrelease.Release{Name: "nginx"}, nil).Once() + + // Create mock KOTS installer + mockInstaller := &MockKotsCLIInstaller{} + mockInstaller.On("Install", mock.Anything).Return(nil) + + // Create manager with in-memory store + appInstallStore := appinstallstore.NewMemoryStore(appinstallstore.WithAppInstall(types.AppInstall{ + Status: types.Status{State: types.StatePending}, + })) + manager, err := NewAppInstallManager( + WithAppInstallStore(appInstallStore), + WithReleaseData(&release.ReleaseData{}), + WithLicense([]byte(`{"spec":{"appSlug":"test-app"}}`)), + WithClusterID("test-cluster"), + WithKotsCLI(mockInstaller), + WithHelmClient(mockHelmClient), + ) + require.NoError(t, err) + + // Install the charts + err = manager.Install(context.Background(), installableCharts, kotsv1beta1.ConfigValues{}) + require.NoError(t, err) + + // Verify that components were registered and have correct status + appInstall, err := manager.GetStatus() + require.NoError(t, err) + + // Should have 2 components + assert.Len(t, appInstall.Components, 2, "Should have 2 components") + + // Components should be sorted by weight (database first with weight 10, web second with weight 20) + assert.Equal(t, "database-chart", appInstall.Components[0].Name) + assert.Equal(t, types.StateSucceeded, appInstall.Components[0].Status.State) + + assert.Equal(t, "web-chart", appInstall.Components[1].Name) + assert.Equal(t, types.StateSucceeded, appInstall.Components[1].Status.State) + + // Overall status should be succeeded + assert.Equal(t, types.StateSucceeded, appInstall.Status.State) + assert.Equal(t, "Installation complete", appInstall.Status.Description) + + mockInstaller.AssertExpectations(t) + mockHelmClient.AssertExpectations(t) + }) + + t.Run("Component failure is tracked correctly", func(t *testing.T) { + // Create test chart + installableCharts := []types.InstallableHelmChart{ + createTestInstallableHelmChart(t, "failing-chart", "1.0.0", "failing-app", "default", 0, map[string]any{"key": "value"}), + } + + // Create mock helm client that fails + mockHelmClient := &helm.MockClient{} + mockHelmClient.On("Install", mock.Anything, mock.MatchedBy(func(opts helm.InstallOptions) bool { + return opts.ReleaseName == "failing-app" + })).Return((*helmrelease.Release)(nil), errors.New("helm install failed")) + + // Create manager with in-memory store + appInstallStore := appinstallstore.NewMemoryStore(appinstallstore.WithAppInstall(types.AppInstall{ + Status: types.Status{State: types.StatePending}, + })) + manager, err := NewAppInstallManager( + WithAppInstallStore(appInstallStore), + WithReleaseData(&release.ReleaseData{}), + WithLicense([]byte(`{"spec":{"appSlug":"test-app"}}`)), + WithClusterID("test-cluster"), + WithHelmClient(mockHelmClient), + ) + require.NoError(t, err) + + // Install the charts (should fail) + err = manager.Install(context.Background(), installableCharts, kotsv1beta1.ConfigValues{}) + require.Error(t, err) + + // Verify that component failure is tracked + appInstall, err := manager.GetStatus() + require.NoError(t, err) + + // Should have 1 component + assert.Len(t, appInstall.Components, 1, "Should have 1 component") + + // Component should be marked as failed + failedComponent := appInstall.Components[0] + assert.Equal(t, "failing-chart", failedComponent.Name) + assert.Equal(t, types.StateFailed, failedComponent.Status.State) + assert.Contains(t, failedComponent.Status.Description, "helm install failed") + + // Overall status should be failed + assert.Equal(t, types.StateFailed, appInstall.Status.State) + + mockHelmClient.AssertExpectations(t) + }) +} diff --git a/api/internal/managers/app/install/status.go b/api/internal/managers/app/install/status.go index 7a3db96b28..5cfc5bc412 100644 --- a/api/internal/managers/app/install/status.go +++ b/api/internal/managers/app/install/status.go @@ -17,3 +17,11 @@ func (m *appInstallManager) setStatus(state types.State, description string) err LastUpdated: time.Now(), }) } + +func (m *appInstallManager) setComponentStatus(componentName string, state types.State, description string) error { + return m.appInstallStore.SetComponentStatus(componentName, types.Status{ + State: state, + Description: description, + LastUpdated: time.Now(), + }) +} diff --git a/api/internal/store/app/install/store.go b/api/internal/store/app/install/store.go index e807bb97a9..17be428050 100644 --- a/api/internal/store/app/install/store.go +++ b/api/internal/store/app/install/store.go @@ -3,6 +3,7 @@ package install import ( "fmt" "sync" + "time" "github.com/replicatedhq/embedded-cluster/api/types" "github.com/tiendc/go-deepcopy" @@ -20,6 +21,8 @@ type Store interface { SetStatusDesc(desc string) error AddLogs(logs string) error GetLogs() (string, error) + SetComponentStatus(componentName string, status types.Status) error + RegisterComponents(componentNames []string) error } // memoryStore is an in-memory implementation of Store @@ -107,3 +110,42 @@ func (s *memoryStore) GetLogs() (string, error) { defer s.mu.RUnlock() return s.appInstall.Logs, nil } + +// SetComponentStatus sets the status of a specific component +func (s *memoryStore) SetComponentStatus(componentName string, status types.Status) error { + s.mu.Lock() + defer s.mu.Unlock() + + // Find and update the component + for i := range s.appInstall.Components { + if s.appInstall.Components[i].Name == componentName { + s.appInstall.Components[i].Status = status + return nil + } + } + + return fmt.Errorf("component %s not found", componentName) +} + +// RegisterComponents initializes the components list with the given component names +func (s *memoryStore) RegisterComponents(componentNames []string) error { + s.mu.Lock() + defer s.mu.Unlock() + + // Clear existing components + s.appInstall.Components = make([]types.AppComponent, 0, len(componentNames)) + + // Initialize each component with pending status + for _, name := range componentNames { + s.appInstall.Components = append(s.appInstall.Components, types.AppComponent{ + Name: name, + Status: types.Status{ + State: types.StatePending, + Description: "", + LastUpdated: time.Now(), + }, + }) + } + + return nil +} diff --git a/api/internal/store/app/install/store_mock.go b/api/internal/store/app/install/store_mock.go index 689373fda1..06557cc1d2 100644 --- a/api/internal/store/app/install/store_mock.go +++ b/api/internal/store/app/install/store_mock.go @@ -53,3 +53,15 @@ func (m *MockStore) GetLogs() (string, error) { args := m.Called() return args.Get(0).(string), args.Error(1) } + +// SetComponentStatus mocks the SetComponentStatus method +func (m *MockStore) SetComponentStatus(componentName string, status types.Status) error { + args := m.Called(componentName, status) + return args.Error(0) +} + +// RegisterComponents mocks the RegisterComponents method +func (m *MockStore) RegisterComponents(componentNames []string) error { + args := m.Called(componentNames) + return args.Error(0) +} diff --git a/api/internal/store/app/install/store_test.go b/api/internal/store/app/install/store_test.go index e83aae151a..9492b474ac 100644 --- a/api/internal/store/app/install/store_test.go +++ b/api/internal/store/app/install/store_test.go @@ -280,3 +280,105 @@ func TestMemoryStore_DeepCopy(t *testing.T) { assert.Equal(t, "Original description", appInstall3.Status.Description) assert.Equal(t, "Original log\n", appInstall3.Logs) } + +func TestMemoryStore_RegisterComponents(t *testing.T) { + store := newMemoryStore() + + // Test registering components + componentNames := []string{"chart1", "chart2", "chart3"} + err := store.RegisterComponents(componentNames) + require.NoError(t, err) + + // Verify components were registered + appInstall, err := store.Get() + require.NoError(t, err) + require.Len(t, appInstall.Components, 3) + + for i, component := range appInstall.Components { + assert.Equal(t, componentNames[i], component.Name) + assert.Equal(t, types.StatePending, component.Status.State) + } +} + +func TestMemoryStore_SetComponentStatus(t *testing.T) { + store := newMemoryStore() + + // Register components first + componentNames := []string{"chart1", "chart2"} + err := store.RegisterComponents(componentNames) + require.NoError(t, err) + + // Test setting component status + status := types.Status{ + State: types.StateRunning, + Description: "Installing chart1", + LastUpdated: time.Now(), + } + err = store.SetComponentStatus("chart1", status) + require.NoError(t, err) + + // Verify component status was updated + appInstall, err := store.Get() + require.NoError(t, err) + assert.Equal(t, types.StateRunning, appInstall.Components[0].Status.State) + assert.Equal(t, "Installing chart1", appInstall.Components[0].Status.Description) + // Second component should remain unchanged + assert.Equal(t, types.StatePending, appInstall.Components[1].Status.State) +} + +func TestMemoryStore_SetComponentStatus_NonExistentComponent(t *testing.T) { + store := newMemoryStore() + + // Try to set status on non-existent component + status := types.Status{ + State: types.StateRunning, + Description: "Installing", + LastUpdated: time.Now(), + } + err := store.SetComponentStatus("nonexistent", status) + assert.Error(t, err) + assert.Contains(t, err.Error(), "component nonexistent not found") +} + +func TestMemoryStore_ComponentStatusConcurrency(t *testing.T) { + store := newMemoryStore() + + // Register components + componentNames := []string{"chart1", "chart2", "chart3"} + err := store.RegisterComponents(componentNames) + require.NoError(t, err) + + var wg sync.WaitGroup + numGoroutines := 10 + numOperations := 20 + + // Concurrent component status operations + wg.Add(numGoroutines * 2) + for i := 0; i < numGoroutines; i++ { + // Concurrent component status writes + go func(id int) { + defer wg.Done() + for j := 0; j < numOperations; j++ { + componentName := componentNames[j%len(componentNames)] + status := types.Status{ + State: types.StateRunning, + Description: "Concurrent update", + LastUpdated: time.Now(), + } + err := store.SetComponentStatus(componentName, status) + assert.NoError(t, err) + } + }(i) + + // Concurrent reads + go func(id int) { + defer wg.Done() + for j := 0; j < numOperations; j++ { + _, err := store.Get() + assert.NoError(t, err) + } + }(i) + } + + wg.Wait() +} diff --git a/api/types/app.go b/api/types/app.go index f5a82572e5..15d0726060 100644 --- a/api/types/app.go +++ b/api/types/app.go @@ -18,10 +18,19 @@ type AppConfigValue struct { // AppConfigValues represents a map of configuration values for the App. type AppConfigValues map[string]AppConfigValue -// AppInstall represents the current state of app installation +// AppInstall represents the current state of app application installation with detailed chart +// components, following the same schema pattern as types.Infra. type AppInstall struct { - Status Status `json:"status"` - Logs string `json:"logs"` + Components []AppComponent `json:"components"` + Status Status `json:"status"` + Logs string `json:"logs"` +} + +// AppComponent represents an individual chart component within the app +// Following the same schema pattern as types.InfraComponent +type AppComponent struct { + Name string `json:"name"` // Chart name + Status Status `json:"status"` // Uses existing Status type } // InstallableHelmChart represents a Helm chart with pre-processed values ready for installation