diff --git a/docs/analisys/colly-to-envgene-mapping.md b/docs/analisys/colly-to-envgene-mapping.md new file mode 100644 index 00000000..5ed582e1 --- /dev/null +++ b/docs/analisys/colly-to-envgene-mapping.md @@ -0,0 +1,111 @@ +# Mapping of Colly attributes to EnvGene attributes + +- [Mapping of Colly attributes to EnvGene attributes](#mapping-of-colly-attributes-to-envgene-attributes) + - [Description](#description) + - [Mapping table](#mapping-table) + - [`cluster-metadata.yml` example](#cluster-metadatayml-example) + - [`env_definition.yml` example](#env_definitionyml-example) + - [To Discuss](#to-discuss) + - [To Implement](#to-implement) + +## Description + +This document details how Colly attributes are persisted within the EnvGene Instance repository, specifying in which files and sections each attribute is stored. + +## Mapping table + +| Colly Object | Colly Attribute | Attribute Type in Colly | EnvGene Repository | Location in EnvGene | Description | +|--------------|-------------------------|---------------------------------------------------------------------------------------------------|--------------------|-------------------------------------------------|-------------------------------------------------------------------------------------------------------------| +| Environment | `name` | string | instance | folder name in `/` | Environment name | +| Environment | `owners` | array of string | instance | `env_definition.metadata.owners` | Users responsible for the environment (Owners in Colly) | +| Environment | `teams` | array of string | instance | `env_definition.metadata.teams` | List of teams assigned to the environment | +| Environment | `status` | enum [`IN_USE`, `RESERVED`, `FREE`, `MIGRATING`, `DEPRECATED`] | instance | `env_definition.metadata.status` | Current status of the environment: in use, free, reserved, etc | +| Environment | `expirationDate` | string (LocalDate "yyyy-MM-dd") | instance | `env_definition.metadata.expirationDate` | Date until which the environment is allocated (expirationDate) | +| Environment | `type` | enum [`ENVIRONMENT`, `CSE_TOOLSET`, `DESIGN_TIME`, `APP_DEPLOYER`, `INFRASTRUCTURE`, `UNDEFINED`] | instance | `env_definition.metadata.type` | Technical category of the environment; type of environment | +| Environment | `role` | string (the list of allowed values is set via deployment parameter) | instance | `env_definition.metadata.role` | Usage role of the environment (e.g. Dev, QA), set by parameters | +| Environment | `labels` | array of string | instance | `env_definition.metadata.labels` | User-defined labels/tags for the environment | +| Environment | `description` | string | instance | `env_definition.metadata.description` | Free-form environment description | +| Environment | `accessGroups` | array of string | instance | `env_definition.security.accessGroups` | List of user groups that can work with the environment | +| Environment | `effectiveAccessGroups` | array of string | instance | `env_definition.security.effectiveAccessGroups` | Resolved full list of user groups (contains groups and their descendants). resolved based on `accessGroups` | +| Cluster | `name` | string | instance | folder name in `/` | Cluster name | +| Cluster | `description` | string | instance | `cluster-metadata.metadata.description` | Free-form cluster description | +| Cluster | `roAdGroups` | string | instance | `cluster-metadata.metadata.roAdGroups` | List of ro names and AD user groups under which all clusters of all projects will be managed | +| Cluster | `rwAdGroups` | string | instance | `cluster-metadata.metadata.rwAdGroups` | List of rw names and AD user groups under which all clusters of all projects will be managed | +| Cluster | `owners` | string | instance | `cluster-metadata.metadata.owners` | List of owners of the cluster | + +Colly should support definition of owners names and groups for RW/RO access to clusters on project level in project git repository (same way as on global level) and accordingly provide it in project response. Current process of SaaS clusters provision consider both SaaS and project members to be mentioned in PaaS Zero Touch pipeline. New attributes are required for SSP to fit into existing flow with Provision Empty Cluster operation. + +### `cluster-metadata.yml` example + +location: `/environments//cloud-passport/` + +```yaml +metadata: + # Optional + description: string + # Optional + # groups for RW/RO access to clusters + roAdGroups: list of strings + rwAdGroups: list of strings + owners: list of strings +``` + +### `env_definition.yml` example + +```yaml +security: TBD +metadata: + owners: + - "user1" + - "user2" + teams: + - "team-a" + - "team-b" + status: "IN_USE" + expirationDate: "2024-12-31" + type: "ENVIRONMENT" + role: "Dev" + labels: + - "prod" + - "priority-high" + description: "very important env" + region: cm +``` + +## To Discuss + +- [x] Is `ticketLinks` required? + - The attribute is not needed by users, so it was decided to remove it + +- [x] Checkov [linting errors](https://github.com/Netcracker/qubership-envgene/actions/runs/18886399036/job/53902750553) + - Decided to ignore + +- [x] Add the `deployPostfix` attribute to the Namespace? + - No + +- [x] Remove the `deploymentVersion` attribute from the Environment? + - Yes + +- [x] What is `deploymentStatus`? + - it is removed + +- [x] Do we keep `cleanInstallationDate`? Is it computed based on SD_VERSIONS? + - it will be removed at [MS2](https://github.com/Netcracker/qubership-colly/issues/153) + +- [x] How do we uniquely identify cluster, environment, and namespace in both services? + +- [x] How do we separate the two services? + - Both services should return a unified schema for cluster, environment, and namespace, with all fields, but fill in only their own data. For example, for Environment: + - inventory-service: owners, teams, status, type, role, labels, description, expirationDate + - operational-service: cleanInstallationDate, monitoringData, deploymentStatus, lastSDDeploymentOperation, lastDeployedSDsByType + - ~~Two different models~~ + +- [ ] Where to store the `description` of a Cluster + +- [ ] Using the OpenAPI specification as documentation + - How are descriptions and examples added to the OpenAPI spec? + +## To Implement + +- [ ] Change the formation of the macros `current_env.description` and `current_env.owners` taking into account the metadata section and migration +- [ ] Extend EnvGene `env_definition.yaml` JSON schema diff --git a/docs/analisys/project-repository.md b/docs/analisys/project-repository.md new file mode 100644 index 00000000..8e324928 --- /dev/null +++ b/docs/analisys/project-repository.md @@ -0,0 +1,284 @@ +# Project Repository + +- [Project Repository](#project-repository) + - [Description](#description) + - [Repository Structure](#repository-structure) + - [Defaults](#defaults) + - [\[Defaults\] `parameters.yaml`](#defaults-parametersyaml) + - [\[Defaults\] `credentials.yaml`](#defaults-credentialsyaml) + - [Projects](#projects) + - [\[Projects\] `parameters.yaml`](#projects-parametersyaml) + - [\[Projects\] `credentials.yaml`](#projects-credentialsyaml) + - [Example](#example) + - [To discuss](#to-discuss) + +## Description + +This document describes the structure and contents of the Project repository. + +## Repository Structure + +```text +# AS IS +├── defaults +| └── defaults.yaml|yml +└── projects + └── + └── parameters.yaml|yml +``` + +```text +├── defaults +| ├── parameters.yaml|yml +| └── credentials.yaml|yml +└── clusters +| ├── parameters.yaml|yml +| └── credentials.yaml|yml +└── projects + └── + ├── parameters.yaml|yml + └── credentials.yaml|yml +``` + +### Defaults + +#### [Defaults] `parameters.yaml` + +```yaml +# Optional +# List of rw/ro names and AD user groups under which all clusters of all projects will be created +# these parameters are used when creating a cluster +Used by SSP +clusters: + roAdGroups: list of strings + rwAdGroups: list of strings + owners: list of strings +``` + +#### [Defaults] `credentials.yaml` + +Currently, this file has no contents + +### Projects + +#### [Projects] `parameters.yaml` + +```yaml +# Mandatory +# Name of the customer +# Used by SSP +customerName: string +# Mandatory +# Name of the project +# Used by SSP and Colly +name: string +# Mandatory +# Type of the project +# Used by SSP +type: enum[ project, product] +# Optional +# List of groups with RW access rights to objects of this project +# Used by SSP +accessGroups: + - string +# Optional +# Platform type for clusters in this project +# "ocp" stands for OpenShift, "k8s" for generic Kubernetes +# Used by SSP +clustersPlatform: enum[ ocp, k8s ] +# Attribute used for Cloud Passport generation +# Used by SSP +mavenRepoName: string (SSP) +# Optional +# Full URL to the git group where project repositories are located +# На основе этого аттрибута вычисляются repositories[].url +# Или на всех repositories заданы url или задан этот аттрибут +# Used by SSP and Colly +gitGroupUrls: + - region: string + gitGroupUrl: string +# Optional +# Used by SSP and Colly +repositories: + - # Mandatory + # All repositories with type envgeneInstance must be specified because Colly uses them; url is mandatory for them + # Assumption: repository with type envgeneTemplate is only one per project + type: enum[ envgeneInstance, envgeneTemplate, clusterProvision, envProvision, solutionDeploy, DCL ] + # Optional + url: string + # Optional + # Not used now + # Token for repository access + # Pointer to Credential in credentials.yaml + token: creds.get('').secret + # Optional + # If not set, the "default" branch is used (as in GitLab/GitHub) + branch: string + # Optional + # Not used now + # Geographical region associated with the Environment. This attribute is user-defined + # Used in cases where specific `pipeline` repositories need to be used for certain environments + region: string + # Optional + # Only for type: envgeneTemplate + # TODO: discover from repository + envgeneArtifact: + # Mandatory + # Default EnvGene environment template artifact name (application from the application:version notation) + # For example platform:20251215.113905-22 + name: string + # Mandatory + # Template name that is used by default when creating project environments. + # Should be included in templateDescriptorNames + defaultTemplateDescriptorName: string +``` + +#### [Projects] `credentials.yaml` + +Contains [Credential](https://github.com/Netcracker/qubership-envgene/blob/main/docs/envgene-objects.md#credential) objects + +```yaml +: + type: usernamePassword + data: + username: string + password: string +: + type: secret + data: + secret: string +``` + +Example: + +```yaml +customerName: ACME +projectName: ACME-bss +repositories: + - type: envgeneInstance + url: https://git.acme.com/instance + token: instance-cred + - region: offsite-cn + type: pipeline + url: https://git.acme.com/pipeline + token: offsite-cn-pipeline-cred + - region: offsite-mb + type: pipeline + url: https://git.acmemb.com/pipelines + token: offsite-mb-pipeline-cred + - type: envgeneTemplate + url: https://git.acme.com/template + token: template-cred + branches: + - r25.3 + - r25.4 +envgeneTemplates: + envgene-acme: + - main + - dt + - dm +``` + +```yaml +instance-cred: + type: secret + data: + secret: "MGE3MjYwNTQtZGE4My00MTlkLWIzN2MtZjU5YTg3NDA2Yzk0MzlmZmViZGUtYWY4_PF84_ba" +template-cred: + type: secret + data: + secret: "MGE3MjYwNTQtZGE4My00MTlkLWIzN2MtZjU5YTg3NDA2Yzk0MzlmZmViZGUtYWY4_PF84_bb" +offsite-cn-pipeline-cred: + type: secret + data: + secret: "MGE3MjYwNTQtZGE4My00MTlkLWIzN2MtZjU5YTg3NDA2Yzk0MzlmZmViZGUtYWY4_PF84_bb" +offsite-mb-pipeline-cred: + type: secret + data: + secret: "MGE3MjYwNTQtZGE4My00MTlkLWIzN2MtZjU5YTg3NDA2Yzk0MzlmZmViZGUtYWY4_PF84_bb" +``` + +#### Example + +Minimal project + +```yaml +# parameters.yaml +customerName: ACME Corp +name: ACME BSS +type: project +repositories: + - type: envgeneInstance + url: https://gitlab.example.com/acme/instance.git + token: creds.get('gitlab-token').secret + +# credentials.yaml +gitlab-token: + type: secret + data: + secret: dummy-token-value +``` + +Full project + +```yaml +# parameters.yaml +customerName: ACME Corp +name: ACME OSS +type: project +accessGroups: + - devops-team + - developers + - qa +clustersPlatform: k8s +repositories: + - type: envgeneInstance + url: https://gitlab.example.com/acme/instance.git + token: creds.get('gitlab-token').secret + defaultBranch: release/26.1 + region: cn + +# credentials.yaml +gitlab-token: + type: secret + data: + secret: dummy-token-value +``` + +## To discuss + +- [x] Use case for Colly using its own project repository: + 1. Read all projects and extract the URL, token, and branches from the `envgeneInstance` repositories in order to display the environments from these projects. + +- [x] Does each project currently have a different `accessGroups`? + - Yes, it is different + +- [x] Is creating Project post/patch via the Colly API planned? + - Not right now, maybe in the future + +- [x] `projectId` = customerAbbr + projectAbbr + +- [x] Should the Project repository be used as the Maintenance inventory? + - yes + +- [x] Need a mapping from environment to project. For example, an environment attribute `project`. + - Use case: find the DCL pipeline repository by environment + - Requestor - The Customer + +- [x] global configuration + +- [ ] `pipeline` is too generic, we need to specify the exact type of pipeline + +- [x] `accessGroups` is a list of user groups for "clusters" or for Colly? + - list of groups that can work with the project => used for access control in SSP: + - who can see/edit which project/cluster/env + +- [ ] `jiraCustomerName` - is it needed? + +- [x] `envgeneArtifact` + - Short term: + - In the Repository, `envgeneArtifact.name` is set manually in Git + - Long term: + - need to decide whether Colly can discover: + - `envgeneArtifact.name` + - `envgeneArtifact.templateDescriptorNames` diff --git a/docs/analisys/the-customer-requirnments.md b/docs/analisys/the-customer-requirnments.md new file mode 100644 index 00000000..8f79e5e7 --- /dev/null +++ b/docs/analisys/the-customer-requirnments.md @@ -0,0 +1,488 @@ +# API Enhancement according to the Customer requirements + +- [API Enhancement according to the Customer requirements](#api-enhancement-according-to-the-customer-requirements) + - [Description](#description) + - [Environment](#environment) + - [Namespace](#namespace) + - [Cluster](#cluster) + - [Colly instance](#colly-instance) + - [To discuss](#to-discuss) + - [To implement](#to-implement) + +## Description + +This document describes the requirements for Colly from The Customer and the changes they introduce to Colly. + +This is not the full list of attributes for these objects, but only those that will be processed as per The Customer’s requirements. + +## Environment + +| Colly Attribute | Attribute Type | Description | +|---------------------------------------------------|---------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `name` | string | Environment name, cannot be changed after creation | +| `status` | enum [`FREE`, `IN_USE`, `RESERVED`, `DEPRECATED`] | Current status of the Environment | +| `role` | string (the valid values are configured via a deployment parameter) | Defines the usage role of the Environment within the project. The list is configured via a deployment parameter and can be extended. | +| `teams` | list of strings | Teams assigned to the Environment. If there are multiple teams, their names are separated by commas. | +| `owners` | list of strings | People responsible for the Environment. If there are multiple, their names are separated by commas. | +| `deploymentOperations` | list of objects | Array of deployment operations for all namespaces related to the Environment. Each element represents a separate deployment operation that may involve one or more namespaces and consists of multiple SD (Solution Descriptor) deployments. | +| `deploymentOperations[].completedAt` | string, date-time | Time when the deployment operation finished. This operation may involve one or more namespaces that are part of the Environment. The time reflects when all deployment items in this operation completed, regardless of their individual outcomes. | +| `deploymentOperations[].deploymentItems` | list of objects | List of SD (Solution Descriptor) deployments that are part of this deployment operation. Each entry represents a single SD deployment with its details. | +| `deploymentOperations[].deploymentItems[].name` | string | Name and version of the SD in format `name:version`. | +| `deploymentOperations[].deploymentItems[].type` | string | Type of SD (e.g., "product", "project", etc.). | +| `deploymentOperations[].deploymentItems[].mode` | enum [`CLEAN_INSTALL`, `ROLLING_UPDATE`] | Deployment mode for the SD: CLEAN_INSTALL (remove all existing resources before deployment) or ROLLING_UPDATE (deploy on top of existing resources). | +| `accessGroups` | string | List of user groups that can work with the environment | +| `effectiveAccessGroups` | string | Resolved full list of user groups (contains groups and their descendants). resolved based on `accessGroups` | +| `description` | string | Free-form Environment description | +| `namespaces` | list of [Namespace](#namespace) objects | List of associated namespaces | +| `cluster` | [Cluster](#cluster) object | Associated cluster | +| `monitoringData.lastIdpLoginDate` | string, date-time | Time of the last successful login to the IDP associated with the Environment | +| `lastSuccessfulSyncAt` | string | Time of the last successful update information from the cluster | + +## Namespace + +| Colly Attribute | Attribute Type | Description | +|-----------------------------------------|----------------|----------------------------| +| `name` | string | Namespace name | + +## Cluster + +| Colly Attribute | Attribute Type | Description | +|-----------------------------------------|------------------|-------------------------------------------------------------------------------------| +| `name` | string | Cluster name, cannot be changed after creation | +| `lastSuccessfulSyncAt` | string | Time of the last successful update information from the cluster | +| `region` | string | Geographical region associated with the Environment. This attribute is user-defined | + +## Colly instance + +| Colly Attribute | Attribute Type | Description | +|-----------------------------------------|----------------- |----------------------------------------------------------------| +| `clusterSyncSchedule` | string, duration | Period of synchronization with the cluster in ISO 8601 format | +| `repositorySyncSchedule` | string, duration | Period of synchronization with a Repository in ISO 8601 format | + +## To discuss + +- [x] `lastIdpLoginDate` + - Implement as in the old Colly + +- [x] `ticketLinks` + - This is the last deploy ticket ID + - Not needed + +- [x] `type` + + - `type` was previously determined based on labels set by the Cloud Passport Discovery CLI, but this functionality is being removed. + - We will keep the attribute with the ability for users to set it manually. + - Open Questions (OQ): + 1. Should this attribute be computed by Colly (and if so, based on what criteria), or should it be user-defined? + 1. It should be user-defined, selected from a predefined list without ability to extend + 2. Does the customer need this type-based environment categorization? + 1. No, it is not required. + +- [x] `lastDeployedSDsByType` ?? + + - Determines the latest SD of a given type that was successfully deployed to one of the namespaces included in the environment. + - This attribute is used for CI environments. + - Shows the scope of the last successful deployment operation. + - Single field with a complex object: + + ```yaml + : + ``` + + for example + + ```yaml + product: product-sd:1.2.3 + project: my-project:1.2.3 + ``` + + - Mapping of SD type to SD name is specified in the Colly deployment parameters: + + ```yaml + solutionDescriptors: + : + - + ``` + + - Default value: + + ```yaml + solutionDescriptors: + product: + - (?i)product + project: + - (?i)project + ``` + +- [x] `deploymentOperations` + - This attribute contains information obtained from the configMap `sd_versions`. + - This attribute is temporary. In the future, the information source for deployment operations will be replaced by a dedicated service, which will require a change in the parameter model. + - DD deployment is out of scope. + +- [x] `status` + - Propose ![env-state-machine.drawio.png](/docs/images/env-state-machine.drawio.png) + 1. `PLANNED` Planned for deployment, but not yet deployed. It exists only as a record in Colly for planning purposes. + 2. `FREE` The Environment is successfully deployed in the cloud but is not used by anyone; it is ready for use and not scheduled for usage. + 3. `IN_USE` The Environment is successfully deployed in the cloud and is being used by a specific team for specific purposes. + 4. `RESERVED` The Environment is successfully deployed in the cloud and reserved for use by a specific team, but is not currently in use. + 5. `DEPRECATED` The Environment is not used by anyone, and a decision has been made to delete it. + - OQ: + 1. What are the cases? + 2. Should it be extendable? + 1. NO + 3. What is `to be deprecated`? Why do we not have `deprecated`, `deleted`, or `not used` states? + 4. Do we need `MIGRATING` (meaning the upgrade is in progress)? + +- [x] `clusterInfoUpdateInterval`, `clusterInfoUpdateStatus.lastSuccessAt` + 1. What are the main scenarios? + 1. The user wants to check that the cluster status shown in Colly is up to date and matches the real state of the cluster, so they can make decisions. + 1. Solution: + 1. Cluster attribute `clusterInfoUpdateStatus.lastSuccessAt` shows the time of the last successful sync + 2. Colly instance (operational service) attribute `clusterInfoUpdateInterval` shows the sync frequency + 2. The user thinks the cluster status data is outdated and wants to trigger a sync manually. + 1. Solution: We do NOT allow users to trigger the sync manually. + 3. An external system integrated with Colly thinks the data is outdated and wants to trigger sync. + 1. Solution: Use the API `/colly/v2/inventory-service/manual-sync` + 2. Should Colly support a forced `clusterInfoUpdate`, not by schedule but by user request? + 1. The customer will call this API as part of their workflow, but users in the UI will not have access to this. + 3. What is the recommended sync frequency with the cluster? + 1. At least once every 30 minutes. + +- [x] `role` + + - Should it be extendable? + - Currently, `role` values are extended via deployment parameters + - A separate interface to provide the list of roles is needed + - Challenge the predefined list of roles + - [`Dev`, `QA`, `Project CI`, ~~`SaaS`~~, `Joint CI`, ~~`Other`~~, `Pre Sale`] - set via deployment parameters + +- [x] `team` or `teams`? `owner` or `owners`? + - `owners`, `teams` are lists + +- [x] Each POST in the API will result in a separate commit + +- [x] `id` is `uuid`; `name` is `` + +- [x] The mediation layer composes the API between the inventory and operational services + +- [x] It should be possible to get a list of projects + - `/colly/v2/inventory-service/projects` + - returns a summary view: + - projectId + - projectName + - `/colly/v2/inventory-service/projects/Id` + - returns detail view + - The potential problem of a project with two instance repositories will be addressed when it arises + +- [x] It should be possible to get a list of clusters + - `/colly/v2/inventory-service/clusters` + - returns a summary view: + - clusterId + - projectName + - `/colly/v2/inventory-service/projects/Id` + - returns detail view + +- [x] Add the `deployPostfix` attribute to the Namespace? + - No + +- [x] Is it correct to say that a single physical business solution instance, consisting of product and project applications, can be modeled with two EnvGene environments - one for product, one for project? + - No, there is only one Environment. + +- [x] Before creating a cluster, the environment queries Colly to check if there is already a cluster with the same name. + +- [x] If Colly discovers the instance repo and receives a cluster that already exists in Colly (by the `name` attribute), another one is created with the same name but a unique ID. + +- [ ] What should be the scope of synchronization between Colly and the cluster + 1. All clusters of the instance + 2. Individual clusters + 3. Individual environments within a cluster + +- [ ] Lock + - The lock must answer the following questions: + - Status: locked or not locked + - Who locked it: free-form string + - Reason for locking: free-form string + - When it was locked: timestamp + - When it will be unlocked: date + - Required interfaces: + - Set/remove lock on the environment + - Force sync lock status from Git (can be implemented later) + - Only a Colly admin can lock through the UI; users cannot + - OQ: + 1. Should locks be defined by inventory backend? + 2. Who, when and why lock/unlock + 3. What are the cases from SSP? + +- [ ] What does the TheCustomer -> Colly -> EnvGene integration look like when creating an Environment? + +- [ ] How to aggregate DeploymentOperations across the namespaces of the environment? + +- [ ] The SaaS instance of Colly must support: + - How do we roll out a new version of Colly (canary deployment)? + - The mediation layer finds Colly via service mesh + +- [ ] It should be possible to get a list of environments per project + - Implement by adding a search by `projectId` parameter to `/colly/v2/inventory-service/environments` + - It is necessary to introduce the `projectId` attribute to the environment object + - The Customer does NOT require information about which `customerName` an environment belongs to, nor obtaining lists of environments by `customerName` + +- [ ] Add "default values" to Project attributes, for example: + - `app:ver` of the env template artifact + - Template name in the artifact + - Short term: + - Defaults are set manually in Git + Colly returns them as a project attribute + - Long term: + - ability to set them via API appears + - "Default values" are optional for the project + - There should be an ability to set "default values" at the global level. + - Who merges global into project?: + - Colly? + - Who else (fast click)? + - OQ: + - Do you need templateDescriptorNames or defaultTemplateDescriptorName? or both? + +- [ ] Environment groups + - `accessGroups` + - List of groups that can work with the project => used for access control in SSP: + - Who can see/edit which env + - Attribute in env inventory + - There should be an ability to set this attribute: + - When creating env (`env_inventory_generation`) + - `effectiveAccessGroups` + - Resolved full list of groups (contains groups and their descendants) + - Attribute in env inventory + - Need to display the resolved list at the time of env creation => need to save it somewhere + - OQ: + - Who computes `effectiveAccessGroups` + - not Colly + - Where is the `effectiveAccessGroups` cache stored + - in Git + - Only read or also write `effectiveAccessGroups` and `accessGroups` via Colly? + +- [ ] Is information about synchronization with git repositories (inventory service) needed: + + ```yaml + # colly operational service + projectGitInfoUpdateStatus.lastSuccessAt: + # cluster + clusterInfoUpdateStatus.lastSuccessAt: + # env + gitInfoUpdateStatus.lastSuccessAt: + ``` + +- [x] deploymentOperations + + 1. How do we determine CLEAN_INSTALL (ACHKA does not provide this attribute)? + 2. Rename mode CLEAN_INSTALL -> "??" ROLLING_UPDATE -> "??" + + **solution:** remove deploy mode. In the Argo world there is no CLEAN_INSTALL -> there is only one type of deployment. + +- [x] `/colly/inventory-service/v2/projectDefaults` + + This interface returns what is stored in `/defaults/parameters.yaml` in the project Git repo + + ```yaml + clusters: + roAdGroups: list of strings + rwAdGroups: list of strings + owners: list of strings + ``` + +- [x] `MAVEN_REPO_NAME` + + - **Add `mavenRepoName` attribute to Project** + - Do we need MAVEN_REPO_URL? If yes, is it one per project? + - Proposal: put parameters required for passport under a single flexible map + + ```text + MAVEN_REPO_URL: https://artifactorycn.netcracker.com/ -> not needed for now + MAVEN_REPO_NAME: pd.saas-global.mvn.group + ``` + +- [ ] `templateDescriptorNames` discovery from artifact. + - Add a Colly interface that takes app:ver, uses app/reg defaults, downloads the artifact, processes the content, and returns the list of templates in the artifact + - Put in backlog, pick up later + +- [ ] What is revert of UI paramsets? Removal of all overrides for env or env+ns+app? + +- [ ] The user must clearly understand why a Colly attribute value is empty - whether it was not discovered, or is simply empty + +- [ ] If during sync with instance repo: + 1. no `/environments///Inventory/env_definition.yaml|yml` => delete the env + 2. `/environments///Inventory/env_definition.yaml|yml` exists + env is in cache + "validations" failed: + 1. valid yaml + 2. valid against Colly schema + 3. ~~ui paramset association exists, but no paramset~~ + => update `lastSuccessfulSyncAt` status + 3. `/environments///Inventory/env_definition.yaml|yml` exists + env is not in cache + "validations" failed: + 1. valid yaml + 2. valid against Colly schema + 3. ~~ui paramset association exists, but no paramset~~ + => env is created with the fields that can be read + +- [ ] if one paramset failed to save, all paramsets will not be saved + +- [ ] what is the SSP role model, how does it affect Colly (who changes the owner on env, who changes ui paramsets, ...). Integration via shared idp? How is this related to `effectiveAccessGroups` + - перейти на общий idp + +- [ ] `region` on cluster + - `region` - новый аттрибут опциональный, который забирается из клауд паспорта + - RO аттрибут + - удалить с энва + +- [ ] ES + - [ ] нужен ли `ui_override_committed` стейт параметра + - [ ] отображать ли ES для энва или нс (без аппа) + - [ ] отображать деплой и рантайм контекст нужно только для нс + апп + - [ ] отображать пайплайн контекст нужно для всего энва + +- [ ] PS + - [x] если один из парамсетов не может быть создан, ддругие в запросе тоже не создаются + - [ ] у pipeline контекста только энв уровень (нельзя привязать к нс или аппу) + +- [x] cmApproach + + 1. При создание энв деф, SSP дополнительно передает: + + ```yaml + metadata: + cmApproach: enum[cmdb, noCmdb] # Optional + ``` + + 2. В Колли энве появляется новый аттрибут `cmApproach`, который читается из env_definition `metadata.cmApproach`. # Default - Null/Unknown + 1. Через API должна быть возможность изменить значение + + 3. В Колли проекте у репозитория с типом `envgeneInstance` появляется новый аттрибут `defaultCmApproach`. Этот аттрибут используется SSP для того что бы не заставлять пользователя задавать `cmApproach` при создание каждого энва. Этот аттрибут задается пользователем только вручную в гите + + 4. AI: Найти правильное место для cmdb url/login/token для конкретного энва. В ES? + 1. ES генерируется и при `cmdb` подходе + 2. Делаем отдельно от 1-3 + +- [ ] Colly-to-Prod + 1. выдаем версию с этим функционалом + 2. SSP тестирует + 3. эта версия Колли устанавливается на прод + 4. ??? + - **Step 1** + - [x] Проверка перформанса при работе с большим энвген инстансным репозиториев + - [x] `gitGroupUrl` + - [x] Add DCL to `repositories[].type` enum + - [x] Deletion removed **envs** from Colly cash + - [x] Make `repositories[].token` optional + - [x] Remove `repositories[].token` + - move `region` from env to cluster. RO. Get from Cloud Passport + - даблчекнуть с Егором - имя и расположение параметра. + - Deletion removed **project, cluster** from Colly cash + - **Step 2** + - CMDB UI - **обновить!!** + - `/api/v1/environments/{environmentId}/applications` + - paramset + - reset + - complex value support + - добавить валидацию - на неймспейс уровне нельзя задать параметры пайплайн контекста + - ES + - `originalValue` + - кейс удаление заоверрайженного ранее параметра !!! + - repo encryption + - до этого (если crypt: false OR заинкрипченные файлы): + - отдавать на `POST /api/v1/environments/{environmentId}/ui-parameters/effective-set` ошибку + - не кэшировать ES и UI парамсеты + - `cmApproach`, `defaultCmApproach` + - change enum env `status` - `PLANNED`, `PROVISION_FAILED`, `CREATED` + - OQ: Что делаем с `accessGroups`, `effectiveAccessGroups` on Environment? + - переносим из под `env.metadata` куда то? или что то другое? + - Add `lastSuccessfulSyncAt` to environment to inventory service metadata + - Add `owners` to Cluster + - To agree where to store + +- [ ] Cloud Release + - тесты + - создать инстанс, проект репо (есть контент для этих реп) + - Project git repo sample - https://github.com/ormig/project-git-sample + - Instance Repo sample - https://github.com/ormig/cloud-passport-samples.git + - обновить тесты + - promote + - Вероника добавляет Колли в исключение для того что бы при релизе с помощью дженкинс джобы dtrust не падал + - ztd + - [Done] idp configuration + - redis CR + - labels + - access to cloud release + - все сделали, ждем пока предоставят + - добавить кору в депенды + - [Done] получить доступ к клауд релиз кластеру + - проверить redis CR на нашем кластере + - устранить дискрепанси между внутренним и внешним хелмом + +## To implement + +- [x] Change environment attributes + 1. `team`(string) -> `teams`(list of strings) + 2. `owner`(string) -> `owners`(list of strings) +- [ ] `role` + - [x] Add `role` attribute on Environment + - [ ] Add deployment parameter to extend `role` valid values + - [x] Remove default value for `role` + - [ ] Implement an interface (`/colly/operational-service/v2/metadata`) that returns the list of `role` valid values (Low priority) +- [x] `type` + - [x] Remove the functionality for auto-generating the `type` attribute. Users should be able to set this value themselves by selecting from a list of allowed values. The list of values should be specified as a deployment parameter. +- [ ] Include the current Colly API version in the X-API-Version HTTP response header for every API response (Low priority) +- [x] Add `lastIdpLoginDate` attribute (via configurable monitoringData) +- [x] Add deployment parameter for `monitoringData` extension +- [x] Remove `ticketLinks` attribute +- [x] Add `region` attribute +- [x] Add `clusterInfoUpdateInterval`, `clusterInfoUpdateStatus.lastSuccessAt` + remove `synced` **2.5.0** +- [x] Ability to sync per project, not per Colly instance **2.4.0** +- [x] Add `accessGroups` to Project **2.4.0** +- [x] Add `clustersPlatform` to Project +- [x] Add `envgeneArtifact` to Project +- [x] Create default configuration for `monitoringData` +- [ ] Support cred macro +- [x] `numberOfNodes` **2.5.0** +- [ ] The configuration for `monitoringData` is currently shared across all environments - it needs to be made more granular +- [x] Add `accessGroups`, `effectiveAccessGroups` to Environment. **2.3.0** +- [ ] add handling of Redis failure - if it fails, need to re-sync with Git and clusters +- [ ] добавить re-sync with Git and clusters при старте Колли + - [ ] рединес проба Redis probe - completed discovery +- [ ] add anti-affinity rules +- [x] add `deployPostfix` **2.4.0** +- [ ] add BG support for `deployPostfix` +- [ ] Add `owners` to Cluster. Agree where to store +- [ ] Support `inventory.cloudPassport` during Cluster "discovery". Priority is unclear, not doing it yet +- [x] support branch for instance repo **2.5.0** +- [x] Add `deploymentOperations` ( will do via ACHKA in 26.1 ) **-** + - [ ] Remove `cleanInstallationDate` + - [x] Add `ACHKA_URL` finding logic +- [x] Add `mavenRepoName` to Project **2.7.0** +- [x] Add `clusterDefaults` to Project **2.7.0** +- [x] Remove `envgeneArtifact.templateDescriptorNames` from Project +- [ ] add `lastSuccessfulSyncAt` **P2** + - [ ] to Repository + - [x] to inventory service metadata +- [x] Remove `deploymentVersion` and its generation logic +- [ ] Cloud Release **P1** +- [ ] CMDB UI + - [ ] paramset **P3** + - [x] get + - [x] set + - [x] delete paramset and association if POST with empty content is received (add to docs) + - [ ] reset **P4** + - [ ] `GET /api/v1/environments/{environmentId}/applications` + - [ ] complex value support + - [ ] ES **P3.5** + - [ ] `originalValue` + - [ ] кейс удаление заоверайженного ранее параметра !!! + - [ ] repo encryption + - до этого (если crypt: false OR заинкрипченные файлы): + - отдавать на `POST /api/v1/environments/{environmentId}/ui-parameters/effective-set` ошибку + - не кэшировать ES и UI парамсеты +- [ ] Add DCL to `repositories[].type` enum +- [ ] Deletion removed envs from Colly cash +- [x] Add `gitGroupUrl` **-** +- [x] remove roles, relay to idp **-** +- [ ] Make `repositories.url` `repositories.token` optional +- [ ] move `region` from env to cluster. RO. Get from Cloud Passport +- [ ] change enum env `status` - `PLANNED`, `PROVISION_FAILED`, `CREATED` +- [ ] move `accessGroups`, `effectiveAccessGroups` and to security + diff --git a/docs/design/colly-applications-api.md b/docs/design/colly-applications-api.md new file mode 100644 index 00000000..484a2225 --- /dev/null +++ b/docs/design/colly-applications-api.md @@ -0,0 +1,106 @@ +# Applications API для Colly + +- [Applications API для Colly](#applications-api-для-colly) + - [Введение](#введение) + - [API Endpoints](#api-endpoints) + - [GET /api/v1/environments/{environmentId}/applications](#get-apiv1environmentsenvironmentidapplications) + - [Параметры](#параметры) + - [Примеры запросов](#примеры-запросов) + - [Ответы](#ответы) + - [Логика обработки](#логика-обработки) + - [Кэширование](#кэширование) + +## Введение + +Данный документ содержит описание API для получения списка приложений в окружении. Этот API используется UI для отображения выпадающего списка приложений при создании Application-level UI override параметров. + +## API Endpoints + +### GET /api/v1/environments/{environmentId}/applications + +Отдает список имен приложений для заданного `environmentId` и `namespaceName`, полученные из SD, расположенного в: + +```text +/environments///Inventory/solution-descriptor/sd.yaml|yml +``` + +#### Параметры + +- `environmentId` (path, mandatory) - Environment uuid +- `namespaceName` (query, mandatory) - Имя Namespace + +#### Примеры запросов + +```text +GET /api/v1/environments/550e8400-e29b-41d4-a716-446655440000/applications?namespaceName=env-01-core +``` + +#### Ответы + +- `200 OK` - Список приложений найден + - Body: массив строк с именами приложений +- `200 OK` - Приложения не найдены + - Body: `[]` +- `404 Not Found` - Namespace не найден или `deployPostfix` не определен + +**Пример успешного ответа:** + +```json +[ + "my-app-1", + "my-app-2", + "backend-service" +] +``` + +**Пример ответа при отсутствии приложений:** + +```json +[] +``` + +#### Логика обработки + +API работает с кэшированными данными Solution Descriptor. Кэш создается и обновляется по расписанию. + +При получении запроса: + +1. Проверить наличие валидного кэша SD для данного `environmentId`: + - Если кэш существует и валиден → использовать данные из кэша + - Если кэш отсутствует или невалиден → возврат пустого списка с warning в логах + +2. Определение `deployPostfix` для namespace: + - Найти `Namespace` объект по `namespaceName` и `environmentId` + - Получить значение поля `deployPostfix` из `Namespace` объекта + - Если `deployPostfix` не найден для `namespaceName` → `404 Not Found` + +3. Получение списка приложений из кэша: + - Отфильтровать приложения из кэшированного SD по совпадению `deployPostfix` + - Если для данного `deployPostfix` не найдено ни одного приложения → возврат пустого списка + - Вернуть список имен приложений + +#### Кэширование + +Colly кэширует информацию из Solution Descriptor. API работает исключительно с кэшированными данными, не читает SD файлы напрямую + +**Создание и обновление кэша:** + +- **По расписанию:** + - Кэш обновляется автоматически каждые n минут для всех окружений + - Процесс выполняется в фоне, независимо от API запросов + +- **Процесс создания кэша:** + 1. Найти файл SD по пути (попытка `.yaml`, затем `.yml`) + 2. Если файл не найден → warning в логах, кэш не создается + 3. Валидация структуры SD: + - Проверить наличие секции `applications` + - Для каждого приложения проверить обязательные поля: `version`, `deployPostfix` + 4. Если валидация успешна → создать/обновить кэш + +**Инвалидация кэша:** + +- **При кэширование по расписанию:** + - При ошибках валидации SD (отсутствие обязательных полей `version`, `deployPostfix`) + - При отсутствии секции `applications` в SD + - Действие: кэш для данного окружения полностью удаляется, warning в логах + - При следующем periodic sync SD будет прочитан и провалидирован заново diff --git a/docs/design/colly-effective-set-api.md b/docs/design/colly-effective-set-api.md new file mode 100644 index 00000000..d42f214c --- /dev/null +++ b/docs/design/colly-effective-set-api.md @@ -0,0 +1,543 @@ +# Effective Set API для Colly + +- [Effective Set API для Colly](#effective-set-api-для-colly) + - [Введение](#введение) + - [Контекст](#контекст) + - [Effective Set](#effective-set) + - [UI Override](#ui-override) + - [UI Override Original values](#ui-override-original-values) + - [`state`](#state) + - [`value`](#value) + - [`originalValue`](#originalvalue) + - [Правила мержа параметров](#правила-мержа-параметров) + - [Детальное описание API](#детальное-описание-api) + - [POST /api/v1/environments/{environmentId}/effective-set](#post-apiv1environmentsenvironmentideffective-set) + +## Введение + +Данный документ содержит описание Effective Set API для Colly. Этот API используется для получения эффективного набора параметров (Effective Set) с метаданными о состоянии каждого параметра. + +## Контекст + +### Effective Set + +Effective Set - это финальный набор параметров для Environment, сгенерированный Calculator слиянием всех источников параметров (template, inventory, custom params, UI Override). + +Структура каталогов и контексты описаны в [calculator-cli.md](https://github.com/Netcracker/qubership-envgene/blob/main/docs/features/calculator-cli.md#version-20-effective-set-structure). + +### UI Override + +Объект для переопределения параметров Effective Set через UI. + +Расположение в репозитории: + +```text +... +└── environments + └── + └── + └── ui-overrides + ├── deployment.yaml + ├── runtime.yaml + └── pipeline.yaml +``` + +Структура объекта: + +```yaml +environment: hashmap +namespaces: + : hashmap +applications: + : + : hashmap +``` + +### UI Override Original values + +Часть ES, в которых Calculator сохраняет в Git значения тех параметров, которые пользователь переопределяет через UI Override: для каждого такого параметра фиксируется значение, которое он имел бы в Effective Set до применения UI Override. + +Расположение в репозитории: + +```text +... +└── environments + └── + └── + └── effective-set + └── ui-override-original-values + ├── deployment.yaml + ├── runtime.yaml + └── pipeline.yaml +``` + +Структура объекта + +```yaml +environment: hashmap +namespaces: + : hashmap +applications: + : + : hashmap +``` + +### `state` + +Атрибут каждого параметра Effective Set в ответе `POST /api/v1/environments/{environmentId}/effective-set`. + +Параметр может иметь одно из трех состояний: + +1. `ui_override_untouched` - Я не изменял этот параметр в UI + - key/value не задан через UI + - key/value нет в файлах UI Override в Git + - Параметр может быть или не быть в Effective Set + +2. `ui_override_uncommitted` - Я изменил этот параметр в UI, но не закоммитил изменения + - key/value задан в UI + - key/value **нет** в файлах UI Override в Git + - Значение применяется только локально в UI + +3. `ui_override_committed` - Я изменил этот параметр в UI и закоммитил + - key/value задан в UI + - key/value **есть** в файлах UI Override в Git + - Значение применено в Git и будет использовано Calculator при следующей генерации ES + +### `value` + +Атрибут каждого параметра Effective Set в ответе `POST /api/v1/environments/{environmentId}/effective-set`. + +Целевое значение параметра - то, которое окажется в Effective Set после применения UI Override. + +`value` формируется слиянием трёх источников данных в порядке приоритета (от низкого к высокому): + +1. **Effective Set** +2. **UI Override из Git** +3. **Uncommitted параметры** (из запроса) + +### `originalValue` + +Атрибут каждого параметра Effective Set в ответе `POST /api/v1/environments/{environmentId}/effective-set`. + +Оригинальное значение - то, которое было бы в Effective Set без учёта UI Override. + +`originalValue` отвечает на вопрос, с какого значения пользователь отталкивался до изменений в UI. + +**Источники `originalValue`:** + +1. Файлы в `effective-set/ui-override-original-values/` (в зависимости от контекста запроса: `deployment.yaml`, `runtime.yaml` или `pipeline.yaml`). +2. Текущий Effective Set (если для параметра нет записи в этих файлах). + +### Правила мержа параметров + +При формировании Effective Set и определении значений параметров применяются рекурсивные правила мержа (recursive merge): + +- **Для словарей**: рекурсивное объединение (если ключ есть в обоих источниках и значение — словарь, они объединяются рекурсивно) +- **Для списков**: полная замена (список из последнего файла заменяет предыдущий) +- **Для примитивов**: значение из последнего файла перезаписывает предыдущее + +**Порядок приоритетов источников** (от низкого к высокому): + +1. **Effective Set файлы** (базовые параметры) +2. **Файлы UI Override в Git** (закоммиченные изменения) +3. **Uncommitted параметры** (из запроса) + +## Детальное описание API + +### POST /api/v1/environments/{environmentId}/effective-set + +Отдает эффективный набор параметров (Effective Set) с метаданными о состоянии каждого параметра для заданного контекста (deployment/runtime/pipeline). + +**Параметры запроса:** + +- `environmentId` (path, mandatory) - Environment uuid +- `context` (query, mandatory) - Контекст параметров: `deployment`, `runtime`, `pipeline` +- `namespaceName` (query, mandatory для deployment/runtime, не используется для pipeline) - Имя namespace +- `applicationName` (query, mandatory для deployment/runtime, не используется для pipeline) - Имя приложения + +**Request Body:** + +```json +{ + "parameters": {} +} +``` + +**Поля:** + +- `parameters` (object, optional) - параметры из UI. В теле передаётся **полное** содержимое поля (включая уже закоммиченные в Git значения). + +**Объектная модель EffectiveSetParameter:** + +```yaml +## EffectiveSetParameter +_type: enum[container, leaf] +_data: + value: any # Текущее значение параметра (после мержа всех источников) + state: enum[ # Состояние параметра с точки зрения пользователя UI + ui_override_untouched, # Состояние 1: параметр не изменялся через UI Override (untouched) + ui_override_uncommitted, # Состояние 2: задан в UI, но не закоммичен + ui_override_committed # Состояние 3: задан в UI, закоммичен + ] + originalValue: any # Изначальное значение из Effective Set (до любого UI Override). Для ui_override_untouched равно текущему значению +``` + +> [!NOTE] +> Служебные поля `_type` и `_data` используют префикс подчеркивания для предотвращения конфликтов с пользовательскими именами параметров (например, если у пользователя есть параметр с именем `data` или `type`). + +**Примеры запросов:** + +**Deployment context (с незакоммиченными параметрами):** + +```http +POST /api/v1/environments/550e8400-e29b-41d4-a716-446655440000/effective-set?context=deployment&namespaceName=env-01-core&applicationName=my-app +``` + +```json +{ + "parameters": { + "CUSTOM_PARAM": "uncommitted-value", + "backupDaemon": { + "resources": { + "limits": { + "cpu": "400m" + } + } + } + } +} +``` + +**Pipeline context (без незакоммиченных параметров):** + +```http +POST /api/v1/environments/550e8400-e29b-41d4-a716-446655440000/ui-parameters/effective-set?context=pipeline +``` + +```json +{} +``` + +**Runtime context:** + +```http +POST /api/v1/environments/550e8400-e29b-41d4-a716-446655440000/ui-parameters/effective-set?context=runtime&namespaceName=env-01-core&applicationName=backend-service +``` + +```json +{ + "parameters": { + "SERVICE_PORT": "9090" + } +} +``` + +**Ответы:** + +- `200 OK` - Effective Set успешно сформирован + - Body: Effective Set с параметрами и метаданными +- `400 Bad Request` - Некорректные параметры запроса (отсутствует обязательный параметр, неверный контекст) +- `404 Not Found` - Environment, Namespace или Application не найдены + +**Примеры ответов:** + +> [!NOTE] +> Примеры ответов построены на основе следующей YAML структуры параметров из Effective Set: +> +> ```yaml +> backupDaemon: +> data: true +> backupSchedule: "0 0 * * *" +> resources: +> limits: +> cpu: "300m" +> ``` + +**Пример 1: Deployment context с различными состояниями параметров:** + +```json +{ + "context": "deployment", + "environmentId": "550e8400-e29b-41d4-a716-446655440000", + "namespaceName": "env-01-core", + "applicationName": "my-app", + "parameters": { + "backupDaemon": { + "_type": "container", + "_data": { + "data": { + "_type": "leaf", + "_data": { + "value": true, + "state": "ui_override_untouched", + "originalValue": true + } + }, + "backupSchedule": { + "_type": "leaf", + "_data": { + "value": "0 0 * * *", + "state": "ui_override_untouched", + "originalValue": "0 0 * * *" + } + }, + "resources": { + "_type": "container", + "_data": { + "limits": { + "_type": "container", + "_data": { + "cpu": { + "_type": "leaf", + "_data": { + "value": "300m", + "state": "ui_override_untouched", + "originalValue": "300m" + } + } + } + } + } + } + } + } + } +} +``` + +**Пример 2: Deployment context с незакоммиченными изменениями:** + +```json +{ + "context": "deployment", + "environmentId": "550e8400-e29b-41d4-a716-446655440000", + "namespaceName": "env-01-core", + "applicationName": "my-app", + "parameters": { + "backupDaemon": { + "_type": "container", + "_data": { + "data": { + "_type": "leaf", + "_data": { + "value": false, + "state": "ui_override_uncommitted", + "originalValue": true + } + }, + "backupSchedule": { + "_type": "leaf", + "_data": { + "value": "0 2 * * *", + "state": "ui_override_uncommitted", + "originalValue": "0 0 * * *" + } + }, + "resources": { + "_type": "container", + "_data": { + "limits": { + "_type": "container", + "_data": { + "cpu": { + "_type": "leaf", + "_data": { + "value": "500m", + "state": "ui_override_uncommitted", + "originalValue": "300m" + } + } + } + } + } + } + } + } + } +} +``` + +**Пример 3: Deployment context с закоммиченными параметрами:** + +```json +{ + "context": "deployment", + "environmentId": "550e8400-e29b-41d4-a716-446655440000", + "namespaceName": "env-01-core", + "applicationName": "my-app", + "parameters": { + "backupDaemon": { + "_type": "container", + "_data": { + "data": { + "_type": "leaf", + "_data": { + "value": true, + "state": "ui_override_untouched", + "originalValue": true + } + }, + "backupSchedule": { + "_type": "leaf", + "_data": { + "value": "0 3 * * *", + "state": "ui_override_committed", + "originalValue": "0 0 * * *" + } + }, + "resources": { + "_type": "container", + "_data": { + "limits": { + "_type": "container", + "_data": { + "cpu": { + "_type": "leaf", + "_data": { + "value": "400m", + "state": "ui_override_committed", + "originalValue": "300m" + } + } + } + } + } + } + } + } + } +} +``` + +**Пример 4: Пустой Effective Set (когда файлы отсутствуют):** + +```json +{ + "context": "deployment", + "environmentId": "550e8400-e29b-41d4-a716-446655440000", + "namespaceName": "env-01-core", + "applicationName": "new-app", + "parameters": {} +} +``` + +**Логика обработки:** + +API работает с кэшированными данными Effective Set и файлами UI Override в Git. + +При получении запроса: + +1. **Валидация параметров запроса:** + - Проверить наличие обязательных параметров (`environmentId`, `context`) + - Для `deployment`/`runtime` контекстов: проверить наличие `namespaceName` и `applicationName` + - Если параметры некорректны → `400 Bad Request` + +2. **Определение `deployPostfix` (для deployment/runtime):** + - Найти `Namespace` объект по `namespaceName` и `environmentId` + - Получить значение поля `deployPostfix` из `Namespace` объекта + - Если `deployPostfix` не найден → `404 Not Found` + +3. **Чтение Effective Set из кэша:** + + Прочитать Effective Set из кэша. Кэш содержит результат мержа всех Effective Set файлов: + + - **Deployment context**: `deployment-parameters.yaml`, `credentials.yaml`, `collision-deployment-parameters.yaml`, `collision-credentials.yaml` (по возрастанию приоритета) + - **Runtime context**: `parameters.yaml`, `credentials.yaml` (по возрастанию приоритета) + - **Pipeline context**: `parameters.yaml`, `credentials.yaml` (по возрастанию приоритета) + + **Обработка credentials:** + - SOPS метаданные (поле `sops`) удаляются из корня YAML + - Если файл был зашифрован (поле `sops` присутствовало), все значения заменяются на `"*****"` + +4. **Чтение файлов UI Override из кэша и их мерж:** + + Прочитать из кэша файлы (`ui-overrides/deployment.yaml`, `ui-overrides/runtime.yaml`, `ui-overrides/pipeline.yaml`) и применить мерж между уровнями: + + - **Deployment/Runtime context**: Environment → Namespace → Application (по возрастанию приоритета) + - **Pipeline context**: Environment (мерж не нужен т.к. только один уровень) + + Если файл отсутствует → использовать пустой объект `{}` + +5. **Чтение UI Override Original values из кэша:** + + Прочитать из кэша артефакты **UI Override Original values**, сгенерированные Calculator: `effective-set/ui-override-original-values/deployment.yaml`, `runtime.yaml`, `pipeline.yaml`. + + Если отсутствует нужный файл, либо для deployment/runtime нет ветки `` → ``, либо для pipeline нет соответствующих данных, считать фрагмент пустым и использовать `{}`. + +6. **Определение состояния параметра (state):** + + Для каждого key/value определить состояние на основе его наличия в разных источниках: + + | Параметр в `parameters` (из запроса) | Параметр в UI Override (Git) | State | + |--------------------------------------|------------------------------|---------------------------| + | ❌ Нет | ❌ Нет | `ui_override_untouched` | + | ❌ Нет | ✅ Есть | `ui_override_committed` | + | ✅ Есть | ❌ Нет | `ui_override_uncommitted` | + | ✅ Есть | ✅ Есть | `ui_override_committed` | + + **Примечания:** + - Uncommitted параметры из запроса (`parameters` в request body) имеют приоритет над committed + - Если параметр есть и в uncommitted, и в Git → считается committed (значение из uncommitted будет в `value`, но state = `ui_override_committed`) + +7. **Определение целевого значения параметра (`value`):** + + Целевое значение - то, которое окажется в Effective Set после применения UI Override. + + Порядок источников (от низкого приоритета к высокому): + + 1. **Effective Set** (базовые параметры из кэша) + 2. **UI Override из Git** (закоммиченные изменения) + 3. **Uncommitted параметры** (из request body) + +8. **Определение оригинального значения параметра (`originalValue`):** + + Для каждого параметра определить `originalValue` - то значение, которое было бы в Effective Set без учёта UI Override: + + - **Если для параметра есть запись** в соответствующем файле `ui-override-original-values/*.yaml` → взять значение оттуда + - **Если записи нет**: + - Если параметр в Effective Set → `originalValue` = значение из ES (из шага 3) + - Если параметра нет в ES → `originalValue` = `null` (параметр добавлен через UI Override) + + **Примечания:** + - `originalValue` **всегда присутствует** в ответе для каждого параметра + - Для параметров со state `ui_override_untouched`: `originalValue` = `value` (равны, но оба указываются) + - Для параметров со state `ui_override_uncommitted` или `ui_override_committed`: `originalValue` показывает значение до UI Override + +9. **Формирование структуры ответа с метаданными:** + + Преобразовать каждый параметр в структуру `EffectiveSetParameter`: + + ```json + { + "_type": "leaf" | "container", + "_data": { + "value": <текущее значение>, + "state": "ui_override_untouched" | "ui_override_uncommitted" | "ui_override_committed", + "originalValue": <оригинальное значение> + } + } + ``` + + **Определение `_type`:** + - `leaf` - если значение является примитивом (string, number, boolean, null) или списком + - `container` - если значение является словарем (объектом) + + **Для container:** + - Рекурсивно обработать все вложенные параметры + - Каждый вложенный параметр также имеет `_type` и `_data` + +10. **Возврат ответа:** + - Вернуть сформированный Effective Set с контекстной информацией + +**Особенности обработки:** + +- Если Effective Set в Git отсутствует → возврат пустого `parameters: {}` +- Если файлы UI Override отсутствуют → используются только параметры из Effective Set +- Если `parameters` в запросе пустой или отсутствует → обрабатываются только закоммиченные параметры +- `originalValue` **всегда присутствует** для каждого параметра в ответе: + - Для новых параметров (добавленных через UI Override) `originalValue` = `null` + - Для untouched параметров `originalValue` = `value` (равны, но оба указываются) + - Для uncommitted/committed параметров `originalValue` показывает значение до UI Override +- При обработке **не учитываются** параметры сервисного уровня, которые присутствуют только в deployment контексте: + - Файл `deploy-descriptor.yaml` + - Файлы в директории `per-service-parameters/` diff --git a/docs/design/colly-ui-parameters-api.md b/docs/design/colly-ui-parameters-api.md new file mode 100644 index 00000000..7c562b59 --- /dev/null +++ b/docs/design/colly-ui-parameters-api.md @@ -0,0 +1,547 @@ +# UI Parameters API для Colly + +- [UI Parameters API для Colly](#ui-parameters-api-для-colly) + - [Введение](#введение) + - [Non-functional Requirements](#non-functional-requirements) + - [Контекст](#контекст) + - [Структура файлов UI Override](#структура-файлов-ui-override) + - [Расположение в репозитории](#расположение-в-репозитории) + - [Общие правила API](#общие-правила-api) + - [Определение уровня (level)](#определение-уровня-level) + - [Поддержка контекстов по уровням](#поддержка-контекстов-по-уровням) + - [Детальное описание API](#детальное-описание-api) + - [GET /api/v1/environments/{environmentId}/ui-parameters](#get-apiv1environmentsenvironmentidui-parameters) + - [POST /api/v1/environments/{environmentId}/ui-parameters](#post-apiv1environmentsenvironmentidui-parameters) + - [DELETE /api/v1/environments/{environmentId}/ui-parameters](#delete-apiv1environmentsenvironmentidui-parameters) + +## Введение + +Данный документ содержит описание UI Parameters API для Colly. Этот API используется для управления параметрами UI Override в Effective Set через файлы в каталоге `ui-overrides/`. + +Параметры UI Override создаются и изменяются только через Colly API. + +## Non-functional Requirements + +1. Время ответа на `GET /api/v1/environments/{environmentId}/ui-parameters` должно быть менее 300 мс +2. Валидации `commitMessage` в `POST /api/v1/environments/{environmentId}/ui-parameters` со стороны Colly быть не должно (валидация на стороне UI) +3. `POST /api/v1/environments/{environmentId}/ui-parameters` должны быть транзакционными + +## Контекст + +### Структура файлов UI Override + +Файлы UI Override (`deployment.yaml`, `runtime.yaml`, `pipeline.yaml`) хранятся в YAML с тремя уровнями параметров: + +- **Environment level** - параметры применяются ко всем namespace и application в окружении +- **Namespace level** - параметры применяются ко всем application в namespace +- **Application level** - параметры применяются к конкретному application + +```yaml +environment: hashmap +namespaces: + : hashmap +applications: + : + : hashmap +``` + +**Пример структуры для deployment контекста:** + +```yaml +# Environment уровень +environment: + param_env1: value1 + param_env2: value2 +# Namespace уровень +namespaces: + namespace-01: + param_ns1: value1 + param_ns2: value2 + namespace-02:namespace "test-core") + param_ns3: value3 +# Application уровень +applications: + namespace-01: + app-01: # applicationName + param1: value2 + param4: null # Удаление параметра + app-02: + param5: value5 + namespace-02: + app-03: + param6: value6 +``` + +**Для runtime контекста:** + +```yaml +environment: + runtime_env_param1: value1 +namespaces: + namespace-01: + runtime_ns_param1: value2 +applications: + namespace-01: + app-01: # applicationName + runtime_param1: value1 + runtime_param2: value2 +``` + +**Для pipeline контекста:** + +```yaml +environment: + pipeline_param1: value1 + pipeline_param2: value2 +namespaces: + namespace-01: + pipeline_ns_param1: value3 +``` + +### Расположение в репозитории + +```text +... +└── environments + └── + └── + └── ui-overrides + ├── deployment.yaml + ├── runtime.yaml + └── pipeline.yaml +``` + +## Общие правила API + +### Определение уровня (level) + +Уровень определяется автоматически на основе переданных query параметров: + +- Если не переданы `namespaceName` и `applicationName` → **Environment level** +- Если передан только `namespaceName` → **Namespace level** +- Если переданы оба параметра → **Application level** + +### Поддержка контекстов по уровням + +- **Environment Level:** `deployment`, `runtime`, `pipeline` +- **Namespace Level:** `deployment`, `runtime` (pipeline не поддерживается) +- **Application Level:** `deployment`, `runtime` (pipeline не поддерживается) + +## Детальное описание API + +### GET /api/v1/environments/{environmentId}/ui-parameters + +Получить параметры UI Override для окружения. + +> [!IMPORTANT] +> API работает с кэшированными данными. Colly не обращается к Git репозиторию напрямую при каждом GET запросе. Кэш создается и обновляется по расписанию. + +**Параметры запроса:** + +- `environmentId` (path, mandatory) - Environment uuid +- `namespaceName` (query, optional) - Имя namespace (для Namespace/Application уровня) +- `applicationName` (query, optional) - Имя приложения (для Application уровня) + +**Примеры запросов:** + +```text +# Environment Level +GET /api/v1/environments/550e8400-e29b-41d4-a716-446655440000/ui-parameters + +# Namespace Level +GET /api/v1/environments/550e8400-e29b-41d4-a716-446655440000/ui-parameters?namespaceName=env-01-core + +# Application Level +GET /api/v1/environments/550e8400-e29b-41d4-a716-446655440000/ui-parameters?namespaceName=env-01-core&applicationName=my-app +``` + +**Ответы:** + +- `200 OK` - Успешный запрос (всегда возвращается, даже если файлы UI Override не существуют) + - Body: `map` (структура зависит от уровня, см. ниже) +- `404 Not Found` - Environment, Namespace или Application не найден + +**Пример успешного ответа для Environment Level:** + +```json +{ + "parameters": { + "deployment": { + "KEY1": "value1", + "KEY2": "value2" + }, + "runtime": { + "KEY1": "value1", + "KEY2": "value2" + }, + "pipeline": { + "KEY1": "value1", + "KEY2": "value2" + } + } +} +``` + +**Пример успешного ответа для Namespace/Application Level:** + +```json +{ + "parameters": { + "deployment": { + "KEY1": "value1", + "KEY2": "value2" + }, + "runtime": { + "KEY1": "value1", + "KEY2": "value2" + } + } +} +``` + +**Логика обработки:** + +API работает с кэшированными данными файлов UI Override. Кэш создается и обновляется по расписанию. + +При получении запроса: + +1. **Определить уровень (level)** (см. раздел ["Определение уровня"](#определение-уровня-level)) + +2. **Для Namespace/Application level - получить `deployPostfix`:** + - Найти `Namespace` объект по `namespaceName` и `environmentId` + - Получить значение поля `deployPostfix` из `Namespace` объекта + - Если `deployPostfix` не найден → `404 Not Found` + +3. **Получить параметры из кэша:** + - Для каждого контекста прочитать кэшированные данные из соответствующего файла UI Override: + - `deployment` → кэш `ui-overrides/deployment.yaml` + - `runtime` → кэш `ui-overrides/runtime.yaml` + - `pipeline` → кэш `ui-overrides/pipeline.yaml` + - Извлечь параметры для соответствующего уровня: + - **Environment level**: из секции `environment` + - **Namespace level**: из секции `namespaces[deployPostfix]` + - **Application level**: из секции `applications[deployPostfix][applicationName]` + - Если кэш для файла отсутствует → возврат пустых параметров `{}` для этого контекста с warning в логах + +**Кэширование:** + +Colly кэширует файлы UI Override из Git репозитория. API работает исключительно с кэшированными данными, не читает файлы из репозитория напрямую при каждом GET запросе. + +**Создание и обновление кэша:** + +- **По расписанию:** + - Кэш обновляется автоматически каждые n минут для всех окружений + - Процесс выполняется в фоне, независимо от API запросов + +- **Процесс создания кэша:** + 1. Прочитать файлы UI Override из Git: `ui-overrides/deployment.yaml`, `ui-overrides/runtime.yaml`, `ui-overrides/pipeline.yaml` + 2. Если файл не найден, кэш для этого контекста не создается + 3. Валидация структуры файла: + - Проверить наличие обязательных секций: `environment`, `namespaces`, `applications` + 4. Если валидация успешна → создать/обновить кэш + +**Инвалидация кэша:** + +- **При кэшировании по расписанию:** + - При ошибках валидации файлов UI Override + - При отсутствии обязательных секций + - Действие: кэш для данного контекста удаляется, warning в логах + - При следующем periodic sync файл будет прочитан и провалидирован заново + - При GET запросе невалидный файл рассматривается как отсутствующий → возврат пустого объекта `{}` для соответствующего контекста + +**Примечания:** + +- `deployment` - обязательный в ответе, по умолчанию `{}` +- `runtime` - обязательный в ответе, по умолчанию `{}` +- `pipeline` + - обязательный в ответе только для Environment Level, по умолчанию `{}` + - не включается в ответ для Namespace и Application Level +- `404 Not Found` возвращается только если не найден сам Environment, Namespace или Application (не файлы UI Override) +- Если файл UI Override не существует, соответствующий контекст возвращается `{}` + +### POST /api/v1/environments/{environmentId}/ui-parameters + +Создать или обновить параметры UI Override. + +**Параметры запроса:** + +- `environmentId` (path) - Environment uuid +- `namespaceName` (query, optional) - Имя namespace (для Namespace/Application уровня) +- `applicationName` (query, optional) - Имя приложения (для Application уровня) + +**Request Body:** + +```json +{ + "commitMessage": "string", + "commitUser": "string", + "commitUserEmail": "string", + "parameters": { + "deployment": {}, + "runtime": {}, + "pipeline": {} + } +} +``` + +**Поля:** + +- `commitMessage` (string, mandatory) - Коммит мессадж для коммита в Git репозиторий +- `commitUser` (string, mandatory) - Имя пользователя под которым происходит коммит +- `commitUserEmail` (string, mandatory) - Email пользователя под которым происходит коммит +- `parameters` (object, mandatory) - Параметры для сохранения, содержит контексты `deployment`, `runtime`, `pipeline` + +**Примеры запросов:** + +**Environment Level:** + +```http +POST /api/v1/environments/550e8400-e29b-41d4-a716-446655440000/ui-parameters +``` + +```json +{ + "commitMessage": "FAKE-0000", + "commitUser": "Vasya A. Pupkin", + "commitUserEmail": "Vasya@mail.com", + "parameters": { + "deployment": { + "KEY1": "value1", + "KEY2": "value2" + }, + "runtime": { + "KEY1": "value1", + "KEY2": "value2" + }, + "pipeline": { + "KEY1": "value1", + "KEY2": "value2" + } + } +} +``` + +**Namespace Level:** + +```http +POST /api/v1/environments/550e8400-e29b-41d4-a716-446655440000/ui-parameters?namespaceName=env-01-core +``` + +```json +{ + "commitMessage": "FAKE-0000", + "commitUser": "Vasya A. Pupkin", + "commitUserEmail": "Vasya@mail.com", + "parameters": { + "deployment": { + "KEY1": "value1", + "KEY2": "value2" + }, + "runtime": { + "KEY1": "value1", + "KEY2": "value2" + } + } +} +``` + +**Application Level:** + +```http +POST /api/v1/environments/550e8400-e29b-41d4-a716-446655440000/ui-parameters?namespaceName=env-01-core&applicationName=my-app +``` + +```json +{ + "commitMessage": "FAKE-0000", + "commitUser": "Vasya A. Pupkin", + "commitUserEmail": "Vasya@mail.com", + "parameters": { + "deployment": { + "KEY1": "value1", + "KEY2": "value2" + }, + "runtime": { + "KEY1": "value1", + "KEY2": "value2" + } + } +} +``` + +**Ответы:** + +- `200 OK` - Параметры обновлены (если файлы UI Override уже существовали) + - Body: полный запрос (см. ниже) +- `201 Created` - Параметры созданы (если файлы UI Override ранее отсутствовали) + - Body: полный запрос (см. ниже) +- `400 Bad Request` - Ошибка валидации (например, передан `pipeline` для Namespace/Application Level) +- `404 Not Found` - Environment, Namespace или Application не найден + +**Примеры ответов:** + +**Пример успешного ответа Environment Level:** + +```json +{ + "commitMessage": "FAKE-0000", + "commitUser": "Vasya A. Pupkin", + "commitUserEmail": "Vasya@mail.com", + "parameters": { + "deployment": { + "KEY1": "value1", + "KEY2": "value2" + }, + "runtime": { + "KEY3": "value3" + }, + "pipeline": { + "KEY4": "value4" + } + } +} +``` + +**Пример успешного ответа Namespace/Application Level:** + +```json +{ + "commitMessage": "FAKE-0000", + "commitUser": "Vasya A. Pupkin", + "commitUserEmail": "Vasya@mail.com", + "parameters": { + "deployment": { + "NAMESPACE_PARAM": "namespace-value" + }, + "runtime": { + "RUNTIME_PARAM": "runtime-value" + } + } +} +``` + +**Логика обработки:** + +1. **Валидация запроса:** + - Проверить наличие обязательных полей: `commitMessage`, `commitUser`, `commitUserEmail`, `parameters` + - Проверить существование Environment по `environmentId` + - Если указан `namespaceName` → проверить существование Namespace и получить `deployPostfix` + - Если указан `applicationName` → проверить, что указан `namespaceName` и проверить существование Application + - Если валидация не прошла → `400 Bad Request` или `404 Not Found` + +2. **Определить уровень** (см. раздел ["Определение уровня"](#определение-уровня-level)) + +3. **Валидация контекстов:** + - Для Namespace/Application Level: если передан `pipeline` контекст → `400 Bad Request` с сообщением "Pipeline context is not supported for Namespace/Application level" + +4. **Обновление файлов UI Override:** + - Для каждого контекста из `parameters` (`deployment`, `runtime`, `pipeline`): + - Прочитать соответствующий файл из Git репозитория: `ui-overrides/deployment.yaml`, `ui-overrides/runtime.yaml`, `ui-overrides/pipeline.yaml` + - Если файл не существует - создать с пустыми секциями `environment`, `namespaces`, `applications` + - Выполнить рекурсивный мерж (recursive merge) новых параметров в соответствующую секцию: + - **Environment level**: в секцию `environment` + - **Namespace level**: в секцию `namespaces[deployPostfix]` + - **Application level**: в секцию `applications[deployPostfix][applicationName]` + - Записать обновленный файл обратно в Git репозиторий + +5. **Сохранение в Git:** + - Создать один commit со всеми изменениями в репозитории + - Использовать указанные `commitMessage`, `commitUser`, `commitUserEmail` + - Push изменений в Git репозиторий + - Если возникла ошибка при commit/push → см. [colly-versioning-conflicts.md](./colly-versioning-conflicts.md) + +6. **Инвалидация кэша:** + - Инвалидировать кэш файлов UI Override для данного Environment + - Кэш будет обновлен при следующем periodic sync (обычно в течение нескольких минут) + + > [!NOTE] + > GET запросы могут возвращать старые данные до момента обновления кэша. + > Для получения гарантированно актуальных данных после POST используйте данные из ответа POST endpoint. + +7. **Формирование ответа:** + - Вернуть `200 OK` или `201 Created` с теми же данными, что были в запросе + +### DELETE /api/v1/environments/{environmentId}/ui-parameters + +Удалить **все** параметры UI Override для окружения. Используется в reset кейсе. + +Удаляются все три файла UI Override (`deployment.yaml`, `runtime.yaml`, `pipeline.yaml`) из директории `ui-overrides/` для указанного environment. + +Удаление является транзакционной операцией - либо все изменения применяются, либо ничего. + +**Параметры запроса:** + +- `environmentId` (path, mandatory) - Environment uuid + +**Request Body:** + +```json +{ + "commitMessage": "string", + "commitUser": "string", + "commitUserEmail": "string" +} +``` + +**Поля:** + +- `commitMessage` (string, mandatory) - Коммит мессадж для коммита в Git репозиторий +- `commitUser` (string, mandatory) - Имя пользователя под которым происходит коммит +- `commitUserEmail` (string, mandatory) - Email пользователя под которым происходит коммит + +**Пример запроса:** + +```http +DELETE /api/v1/environments/550e8400-e29b-41d4-a716-446655440000/ui-parameters +``` + +```json +{ + "commitMessage": "Reset all UI Override parameters for environment", + "commitUser": "Vasya A. Pupkin", + "commitUserEmail": "Vasya@mail.com" +} +``` + +**Ответы:** + +- `204 No Content` - Параметры успешно удалены +- `404 Not Found` - Environment не найден +- `400 Bad Request` - Ошибка валидации параметров запроса + +**Логика обработки:** + +1. **Валидация запроса:** + - Проверить наличие обязательных полей: `commitMessage`, `commitUser`, `commitUserEmail` + - Проверить существование Environment по `environmentId` + - Если валидация не прошла → `400 Bad Request` или `404 Not Found` + +2. **Удалить все файлы UI Override:** + + Для каждого контекста (`deployment`, `runtime`, `pipeline`): + + - Удалить соответствующий файл UI Override из Git репозитория: + - `ui-overrides/deployment.yaml` + - `ui-overrides/runtime.yaml` + - `ui-overrides/pipeline.yaml` + - Если файл не существует → пропустить + +3. **Сохранение в Git:** + - Создать один commit со всеми удалениями + - Использовать переданные `commitMessage`, `commitUser`, `commitUserEmail` для коммита + - Push изменений в Git репозиторий + - Если возникла ошибка при commit/push → см. [colly-versioning-conflicts.md](./colly-versioning-conflicts.md) + +4. **Инвалидация кэша:** + - Инвалидировать кэш файлов UI Override для данного Environment + - Следующий periodic sync обновит кэш с новыми изменениями из Git + +5. **Формирование ответа:** + - Вернуть `204 No Content` + +**Особенности:** + +- Если ни один файл UI Override не существовал → операция всё равно считается успешной → возврат `204 No Content` +- **Атомарность**: операция транзакционна - либо все изменения сохраняются, либо ничего +- Удаляются **все** параметры UI Override для environment (для всех уровней: Environment, Namespace, Application) +- После удаления все параметры UI Override сбрасываются, и Effective Set будет формироваться только из базовых источников diff --git a/docs/design/colly-versioning-conflicts.md b/docs/design/colly-versioning-conflicts.md new file mode 100644 index 00000000..0b192042 --- /dev/null +++ b/docs/design/colly-versioning-conflicts.md @@ -0,0 +1,410 @@ +# Версионирование и обработка конфликтов + +- [Версионирование и обработка конфликтов](#версионирование-и-обработка-конфликтов) + - [Введение](#введение) + - [Версионирование через Git](#версионирование-через-git) + - [Commit Hash как версия](#commit-hash-как-версия) + - [HTTP ETag](#http-etag) + - [Сценарии конфликтов](#сценарии-конфликтов) + - [Сценарий 1: Параллельные изменения через UI разными пользователями](#сценарий-1-параллельные-изменения-через-ui-разными-пользователями) + - [Сценарий 2: Одновременное изменение через UI и Git](#сценарий-2-одновременное-изменение-через-ui-и-git) + - [Сценарий 3: Конфликт при Git push](#сценарий-3-конфликт-при-git-push) + - [Обработка конфликтов](#обработка-конфликтов) + - [Оптимистичная блокировка (Optimistic Locking)](#оптимистичная-блокировка-optimistic-locking) + - [Обработка конфликтов при Git push](#обработка-конфликтов-при-git-push) + - [API детали](#api-детали) + - [GET запросы](#get-запросы) + - [POST запросы](#post-запросы) + - [Коды ответов](#коды-ответов) + - [UI обработка конфликтов](#ui-обработка-конфликтов) + - [Сценарий: Получение 409 Conflict](#сценарий-получение-409-conflict) + - [Сценарий: Получение 412 Precondition Failed](#сценарий-получение-412-precondition-failed) + - [Примеры](#примеры) + - [Пример 1: Успешное обновление с ETag](#пример-1-успешное-обновление-с-etag) + - [Шаг 1: GET запрос](#шаг-1-get-запрос) + - [Шаг 2: POST запрос с If-Match](#шаг-2-post-запрос-с-if-match) + - [Пример 2: Конфликт версий (412)](#пример-2-конфликт-версий-412) + - [Пример 3: Конфликт при Git push (409)](#пример-3-конфликт-при-git-push-409) + +## Введение + +Данный документ описывает механизмы версионирования и обработки конфликтов при работе с UI override параметрами через Colly API. + +Для понимания UI Parameters API см. [colly-ui-parameters-api.md](./colly-ui-parameters-api.md). + +## Версионирование через Git + +### Commit Hash как версия + +Каждое изменение UI override параметров создает новый commit в Git репозитории. Commit hash используется как версия ресурса. + +- **Версия файла** = Git commit hash последнего коммита, изменившего этот файл +- Commit hash является уникальным идентификатором состояния файла +- При каждом изменении создается новый commit с новым hash + +### HTTP ETag + +Colly использует [HTTP ETag](https://datatracker.ietf.org/doc/html/rfc7232#section-2.3) для реализации оптимистичной блокировки: + +- `ETag` header в GET ответе содержит commit hash файла +- `If-Match` header в POST запросе содержит ожидаемый commit hash +- Если версии не совпадают - операция отклоняется с кодом `412 Precondition Failed` + +**Пример:** + +```http +# GET запрос +GET /api/v1/environments/550e8400-e29b-41d4-a716-446655440000/ui-parameters?context=deployment&level=application&namespaceName=ns-01&applicationName=app-01 + +# Ответ +HTTP/1.1 200 OK +ETag: "a1b2c3d4e5f6" +Content-Type: application/json + +{ + "parameters": { + "param1": "value1" + } +} +``` + +```http +# POST запрос с If-Match +POST /api/v1/environments/550e8400-e29b-41d4-a716-446655440000/ui-parameters?context=deployment&level=application&namespaceName=ns-01&applicationName=app-01 +If-Match: "a1b2c3d4e5f6" + +{ + "parameters": { + "param1": "new_value" + }, + "commitUser": "John Doe", + "commitUserEmail": "john.doe@example.com" +} +``` + +## Сценарии конфликтов + +### Сценарий 1: Параллельные изменения через UI разными пользователями + +**Ситуация:** + +1. Пользователь A и Пользователь B одновременно открывают UI и загружают параметры (GET запрос) +2. Оба получают одинаковую версию (ETag = `v1`) +3. Пользователь A изменяет параметр `param1` и сохраняет (POST с `If-Match: v1`) +4. Colly создает commit, новая версия = `v2` +5. Пользователь B изменяет параметр `param2` и пытается сохранить (POST с `If-Match: v1`) + +**Результат:** + +- POST запрос Пользователя B отклоняется с `412 Precondition Failed` +- Пользователь B должен обновить данные (GET запрос) и повторить изменения + +### Сценарий 2: Одновременное изменение через UI и Git + +**Ситуация:** + +1. Пользователь A загружает параметры через UI (GET запрос, ETag = `v1`) +2. Администратор вручную редактирует файл UI override в Git и делает commit (новая версия = `v2`) +3. Пользователь A изменяет параметры в UI и пытается сохранить (POST с `If-Match: v1`) + +**Результат:** + +- POST запрос отклоняется с `412 Precondition Failed` +- Пользователь A должен обновить данные (GET запрос) и увидит изменения администратора + +### Сценарий 3: Конфликт при Git push + +**Ситуация:** + +1. Colly получает POST запрос от Пользователя A +2. Colly создает локальный commit успешно (версия = `v2`) +3. Colly пытается выполнить push в remote репозиторий +4. Push отклоняется, т.к. кто-то другой уже запушил изменения в этот же файл + +**Результат:** + +- Colly откатывает локальный commit +- Colly выполняет pull последних изменений из remote +- Colly возвращает `409 Conflict` с текущей версией из remote +- Пользователь A должен повторить запрос с новыми данными + +## Обработка конфликтов + +### Оптимистичная блокировка (Optimistic Locking) + +Colly использует оптимистичную блокировку для предотвращения конфликтов: + +1. **Клиент читает данные:** + - GET запрос возвращает данные + ETag (commit hash) + - Клиент сохраняет ETag + +2. **Клиент изменяет данные:** + - POST запрос включает `If-Match` header с сохраненным ETag + - Сервер проверяет совпадение версий + +3. **Сервер валидирует версию:** + - Если версии совпадают → операция выполняется + - Если версии не совпадают → `412 Precondition Failed` + +**Преимущества:** + +- Не требует блокировок в БД или файловой системе +- Позволяет нескольким пользователям работать параллельно +- Конфликты обнаруживаются только при сохранении + +### Обработка конфликтов при Git push + +**Алгоритм:** + +1. Colly получает POST запрос +2. Colly валидирует `If-Match` (если указан) +3. Colly создает локальный commit +4. Colly пытается выполнить push в remote: + - **Успех:** commit hash включается в ответ, `200 OK` или `201 Created` + - **Ошибка (кто-то запушил раньше):** + 1. Откатить локальный commit + 2. Выполнить pull из remote + 3. Получить актуальную версию файла + 4. Вернуть `409 Conflict` с текущими данными из remote + +**Пример ответа 409 Conflict:** + +```json +{ + "error": "Conflict", + "message": "The resource has been modified by another user. Please reload and try again.", + "currentVersion": { + "commitHash": "f6e5d4c3b2a1", + "parameters": { + "param1": "value_from_remote", + "param2": "value2" + } + } +} +``` + +## API детали + +### GET запросы + +**Response headers:** + +```http +HTTP/1.1 200 OK +ETag: "" +Content-Type: application/json +``` + +- `ETag` содержит commit hash последнего изменения UI override файла +- Клиент должен сохранить ETag для последующих POST запросов + +### POST запросы + +**Request headers (optional):** + +```http +POST /api/v1/environments/{environmentId}/ui-parameters +If-Match: "" +Content-Type: application/json +``` + +- `If-Match` содержит ожидаемый commit hash +- Если указан - Colly проверяет совпадение версий перед изменением +- Если не указан - изменения применяются без проверки версии (не рекомендуется) + +**Response headers (успех):** + +```http +HTTP/1.1 200 OK +ETag: "" +Content-Type: application/json +``` + +- `ETag` содержит новый commit hash после сохранения +- Клиент должен обновить сохраненный ETag + +### Коды ответов + +| Код | Описание | Когда возникает | +|:---------------------------|:----------------------|:-----------------------------------------------------------| +| `200 OK` | Успешное обновление | POST запрос успешно обновил существующие параметры | +| `201 Created` | Успешное создание | POST запрос создал новые параметры (файл создан впервые) | +| `400 Bad Request` | Некорректный запрос | Отсутствуют обязательные параметры или некорректный формат | +| `404 Not Found` | Ресурс не найден | Namespace или Application не существуют | +| `409 Conflict` | Конфликт при Git push | Кто-то другой запушил изменения в файл раньше | +| `412 Precondition Failed` | Версия не совпадает | ETag из `If-Match` не совпадает с текущей версией файла | +| `422 Unprocessable Entity` | Ошибка валидации | Некорректные значения параметров | + +## UI обработка конфликтов + +### Сценарий: Получение 409 Conflict + +**Шаги для UI:** + +1. Показать сообщение пользователю - Параметры были изменены другим пользователем. Ваши изменения не были сохранены. + +2. Отобразить текущее содержимое из ответа (`currentVersion.parameters`) + +3. Предложить пользователю варианты: + - **Reload** - перезагрузить данные с сервера (выполнить GET запрос) + - **Overwrite** - перезаписать изменения другого пользователя (повторить POST с новым ETag) + - **Merge** - попытаться смержить изменения вручную + +4. После выбора пользователя: + - Если **Reload** - выполнить GET запрос, обновить UI + - Если **Overwrite** - выполнить POST с ETag из `currentVersion.commitHash` + - Если **Merge** - показать diff и позволить пользователю разрешить конфликты + +### Сценарий: Получение 412 Precondition Failed + +**Шаги для UI:** + +1. Показать сообщение пользователю - Параметры были изменены. Пожалуйста, обновите данные. + +2. Автоматически выполнить GET запрос для получения актуальных данных + +3. Обновить UI с новыми данными + +4. Предложить пользователю повторить изменения + +## Примеры + +### Пример 1: Успешное обновление с ETag + +#### Шаг 1: GET запрос + +```http +GET /api/v1/environments/550e8400-e29b-41d4-a716-446655440000/ui-parameters?context=deployment&level=application&namespaceName=ns-01&applicationName=app-01 + +HTTP/1.1 200 OK +ETag: "a1b2c3d4e5f6" +Content-Type: application/json + +{ + "context": "deployment", + "level": "application", + "environmentId": "550e8400-e29b-41d4-a716-446655440000", + "namespaceName": "ns-01", + "applicationName": "app-01", + "parameters": { + "param1": "value1" + } +} +``` + +#### Шаг 2: POST запрос с If-Match + +```http +POST /api/v1/environments/550e8400-e29b-41d4-a716-446655440000/ui-parameters?context=deployment&level=application&namespaceName=ns-01&applicationName=app-01 +If-Match: "a1b2c3d4e5f6" +Content-Type: application/json + +{ + "parameters": { + "param1": "new_value", + "param2": "value2" + }, + "commitUser": "John Doe", + "commitUserEmail": "john.doe@example.com" +} +``` + +**Ответ:** + +```http +HTTP/1.1 200 OK +ETag: "f6e5d4c3b2a1" +Content-Type: application/json + +{ + "status": "updated", + "context": "deployment", + "level": "application", + "environmentId": "550e8400-e29b-41d4-a716-446655440000", + "namespaceName": "ns-01", + "applicationName": "app-01", + "parameters": { + "param1": "new_value", + "param2": "value2" + }, + "commitHash": "f6e5d4c3b2a1", + "commitMessage": "Update UI override parameters for deployment/application", + "commitUser": "John Doe", + "commitUserEmail": "john.doe@example.com" +} +``` + +### Пример 2: Конфликт версий (412) + +**Ситуация:** ETag устарел (кто-то уже изменил файл) + +**POST запрос:** + +```http +POST /api/v1/environments/550e8400-e29b-41d4-a716-446655440000/ui-parameters?context=deployment&level=application&namespaceName=ns-01&applicationName=app-01 +If-Match: "a1b2c3d4e5f6" +Content-Type: application/json + +{ + "parameters": { + "param1": "new_value" + }, + "commitUser": "John Doe", + "commitUserEmail": "john.doe@example.com" +} +``` + +**Ответ:** + +```http +HTTP/1.1 412 Precondition Failed +ETag: "f6e5d4c3b2a1" +Content-Type: application/json + +{ + "error": "Precondition Failed", + "message": "The resource version does not match. Expected version: a1b2c3d4e5f6, actual version: f6e5d4c3b2a1", + "expectedVersion": "a1b2c3d4e5f6", + "actualVersion": "f6e5d4c3b2a1" +} +``` + +### Пример 3: Конфликт при Git push (409) + +**Ситуация:** Локальный commit создан успешно, но push отклонен (кто-то запушил раньше) + +**POST запрос:** + +```http +POST /api/v1/environments/550e8400-e29b-41d4-a716-446655440000/ui-parameters?context=deployment&level=application&namespaceName=ns-01&applicationName=app-01 +If-Match: "a1b2c3d4e5f6" +Content-Type: application/json + +{ + "parameters": { + "param1": "new_value" + }, + "commitUser": "John Doe", + "commitUserEmail": "john.doe@example.com" +} +``` + +**Ответ:** + +```http +HTTP/1.1 409 Conflict +ETag: "x9y8z7w6v5u4" +Content-Type: application/json + +{ + "error": "Conflict", + "message": "The resource has been modified by another user. Please reload and try again.", + "currentVersion": { + "commitHash": "x9y8z7w6v5u4", + "parameters": { + "param1": "value_from_another_user", + "param2": "value2" + } + } +} +``` diff --git a/docs/design/override-ui-prototype-1b.html b/docs/design/override-ui-prototype-1b.html new file mode 100644 index 00000000..500e543b --- /dev/null +++ b/docs/design/override-ui-prototype-1b.html @@ -0,0 +1,2225 @@ + + + + + + EnvGene UI - Option 1B: Extended Levels + + + + + + + + + + + + + + + +
+ +
+
+
+
+ + +
+
+ + +
+
+
+
+ + + + + + + + + Docs + +
+
+
+ + ? +
+
+ Please add a ticket ID to your commit message (for example, ACME-123) +
+
+
+ + ? +
+
+
+
+ + +
+
+
+
+ +
+
+
+ + + +
+ + +
+
+
+
+ Valid YAML +
+
+
+
+ +
+
+
+
+ + +
+
+
+
+ Valid YAML +
+
+
+
+ +
+
+
+
+ + +
+
+
+
+ Valid YAML +
+
+
+
+ +
+
+
+
+
+
+ + ? + + View Environment in Git + + ? +
+
+
+
+
+ + + + + + + + + + diff --git a/docs/design/override-ui-prototype.html b/docs/design/override-ui-prototype.html new file mode 100644 index 00000000..0b5dc855 --- /dev/null +++ b/docs/design/override-ui-prototype.html @@ -0,0 +1,1361 @@ + + + + + + EnvGene UI - Solution 5: Hybrid Approach + + + + + + + + + + + + + + + +
+ +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + + + + + + + + Docs + +
+
+
+ + ? +
+
+ Please add a ticket ID to your commit message (for example, ACME-123) +
+
+
+ + ? +
+
+
+
+ + +
+
+
+
+ +
+
+
+ + + +
+ + +
+
+
+
+ Valid YAML +
+
+
+
+ +
+
+
+
+ + +
+
+
+
+ Valid YAML +
+
+
+
+ +
+
+
+
+ + +
+
+
+
+ Valid YAML +
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+ + + + + + diff --git a/docs/design/ui-override.md b/docs/design/ui-override.md new file mode 100644 index 00000000..814fa094 --- /dev/null +++ b/docs/design/ui-override.md @@ -0,0 +1,223 @@ +# UI Override + +## Концепция + +UI Override - это механизм переопределения параметров Effective Set через UI, который: + +- Применяется Calculator при генерации Effective Set +- Имеет приоритет выше ParamSet'ов и Predefined parameters, но ниже Custom Params +- Поддерживает контексты Effective Set: deployment, runtime, pipeline +- Хранится в трех файлах (по одному на контекст) + +### UI Override Object + +Объект для переопределения параметров Effective Set через UI. + +**Расположение в репозитории:** + +```text +... +└── environments + └── + └── + └── ui-overrides + ├── deployment.yaml + ├── runtime.yaml + └── pipeline.yaml +``` + +**Структура объекта:** + +```yaml +# Environment уровень (применяется ко всем namespace и application) +environment: hashmap +# Namespace уровень (применяется ко всем application в namespace) +namespaces: + : hashmap +# Application уровень (применяется к конкретному application) +applications: + : + : hashmap +``` + +**Пример: `ui-overrides/deployment.yaml`** + +```yaml +environment: + param_env1: value1 + param_env2: value2 +namespaces: + deployPostfix-01: + param_ns1: value1 + param_ns2: value2 + namespace-02: + param_ns3: value3 +applications: + deployPostfix-01: + app-01: + param1: value2 + param4: null # Удаление параметра + app-02: + param5: value5 + namespace-02: + app-03: + param6: value6 +``` + +**Пример: `ui-overrides/pipeline.yaml`** + +```yaml +environment: + pipeline_param1: value1 + pipeline_param2: value2 + +namespaces: + deployPostfix-01: (если нужны namespace-specific параметры для pipeline) + pipeline_ns_param1: value3 +``` + +### UI Override Original values + +Часть ES, в которых Calculator сохраняет в Git значения тех параметров, которые пользователь переопределяет через UI Override: для каждого такого параметра фиксируется значение, которое он имел бы в Effective Set до применения UI Override. + +Расположение в репозитории: + +```text +... +└── environments + └── + └── + └── effective-set + └── ui-override-original-values + ├── deployment.yaml + ├── runtime.yaml + └── pipeline.yaml +``` + +Структура объекта + +```yaml +environment: hashmap +namespaces: + : hashmap +applications: + : + : hashmap +``` + +## Роли Calculator и Colly + +### Calculator + +**Инпуты:** + +1. те что сейчас +2. **UI Override Object** - Calculator ищет UI Override файлы по контрактному пути `/ui-overrides/` (опционально, если директория не существует - пропускается) + +**Что делает:** + +1. Генерирует Effective Set (как и сейчас) +2. Если UI Override Object существует - применяет оверрайды и сохраняет **UI Override Original values** в каталоге `effective-set/ui-override-original-values/`. + +**Приоритет мержа (низкий → высокий):** + +```text +... +- UI Override (environment level) ← НОВОЕ (опционально) +- UI Override (namespace level) ← НОВОЕ (опционально) +- UI Override (application level) ← НОВОЕ (опционально) +- Custom Params (--custom-params) ← highest priority +``` + +**Аутпут в Git (UI Override Original values):** + +```text +effective-set/ + ui-override-original-values/ + deployment.yaml + runtime.yaml + pipeline.yaml +``` + +```yaml +environment: hashmap +namespaces: + : hashmap +applications: + : + : hashmap +``` + +Calculator автоматически ищет UI Override файлы по пути `/ui-overrides/`. Если директория существует - применяет оверрайды, если нет - пропускает этот шаг. + +### Colly + +**Читает из Git/кэша:** + +1. Effective Set (артефакты в `effective-set/`) +2. **UI Override Original values** - файлы в `effective-set/ui-override-original-values/` +3. **UI Override** - файлы в `ui-overrides/` + +**Возвращает через API:** + +1. Effective Set +2. UI Override + +**Вычисляет для каждого параметра Effective Set:** + +#### 1. `originalValue` + +Значение параметра до применения UI Override: + +- Если параметр есть в соответствующем файле **UI Override Original values** (`deployment.yaml` / `runtime.yaml` / `pipeline.yaml`) → берем значение оттуда +- Иначе → берем значение из текущего Effective Set (параметр не переопределялся через UI Override в сохранённых артефактах) + +#### 2. `state` + +Состояние параметра относительно UI Override: + +- `ui_override_uncommitted` - параметр изменен в UI, но не закоммичен +- `ui_override_committed` - параметр закоммичен в Git в UI Override +- `ui_override_untouched` - параметр отсутствует в `request.parameters` и UI Override + +#### 3. `value` + +Целевое значение параметра: + +- Если параметр изменен в UI (есть в `request.parameters`) → берем значение из `request.parameters` +- Иначе, если параметр есть в `ui-overrides/*.yaml` → берем значение из UI Override +- Иначе → берем значение из Effective Set + +**Отдает в API response:** + +- `value` - целевое значение (что будет в Effective Set после коммита) +- `state` - `ui_override_untouched` | `ui_override_uncommitted` | `ui_override_committed` +- `originalValue` - значение до UI Override + +## Реализация + +Calculator + +1. Реализовать поиск UI Override файлов по контрактному пути `/ui-overrides/`: + - `deployment.yaml` для deployment контекста + - `runtime.yaml` для runtime контекста + - `pipeline.yaml` для pipeline контекста +2. Если директория `ui-overrides/` не существует - пропустить этот шаг (UI Override опционален) +3. Если UI Override файлы найдены: + 1. Применять UI Override с приоритетом ниже --custom-params + 2. Генерировать каталог `effective-set/ui-override-original-values/` с файлами `deployment.yaml`, `runtime.yaml`, `pipeline.yaml` (запоминать originalValue перед применением UI Override) + +Colly + +1. Читать файлы UI Override из Git (`ui-overrides/deployment.yaml`, `runtime.yaml`, `pipeline.yaml`) + 1. Парсить структуру `environment` / `namespaces` / `applications` +2. Читать UI Override Original values из `effective-set/ui-override-original-values/` +3. Вычислять state на основе request.parameters, UI Override, UI_OVERRIDE_ORIGINAL_VALUES +4. Вычислять value и originalValue +5. API для коммита request.parameters → UI Override файлы в Git + +Подробнее см.: + +- [colly-effective-set-api.md](./colly-effective-set-api.md) - API для работы с Effective Set +- [colly-ui-parameters-api.md](./colly-ui-parameters-api.md) - API для работы с файлами UI Override +- [colly-versioning-conflicts.md](./colly-versioning-conflicts.md) - Версионирование и конфликты diff --git a/docs/images/colly-model.png b/docs/images/colly-model.png new file mode 100644 index 00000000..771c909e Binary files /dev/null and b/docs/images/colly-model.png differ diff --git a/docs/images/env-state-machine.drawio.png b/docs/images/env-state-machine.drawio.png new file mode 100644 index 00000000..180147df Binary files /dev/null and b/docs/images/env-state-machine.drawio.png differ