diff --git a/graduated/gitops-argocd-sync/.gitignore b/graduated/gitops-argocd-sync/.gitignore new file mode 100644 index 000000000..0c78a1171 --- /dev/null +++ b/graduated/gitops-argocd-sync/.gitignore @@ -0,0 +1,2 @@ +env_vars_to_export +vars.sh diff --git a/graduated/gitops-argocd-sync/CHANGELOG.md b/graduated/gitops-argocd-sync/CHANGELOG.md new file mode 100644 index 000000000..84469f41c --- /dev/null +++ b/graduated/gitops-argocd-sync/CHANGELOG.md @@ -0,0 +1,42 @@ +# Changelog + +## [1.4.5] - 2024-04-04 +### Fixed +- fixing CVEs +- upgrade requirements.txt +- install Python modules locally + +## [1.4.4] - 2024-03-07 +### Fixed +- Do not sync an application in auto-sync mode +- Check for application existence before anything is done + +## [1.4.3] - 2024-02-22 +### Fixed +- Intercepting application not found for better error message + +### Changed +- Move the creation of the link to the application earlier +- Exit with error when app is in OUT_OF_SYNC state + +## [1.4.2] - 2024-01-17 +### Changed +- New graphql call to speed up query + +## [1.4.1] - 2023-10-31 +### Changed +- Add CA_BUNDLE option + +## [1.4.0] - 2023-10-30 +### Changed +- Add INSECURE option + +## [1.3.1] - 2023-09-18 +### Fixed +- CVE-2023-37920 - upgrade Python module certifi to 2023.7.22 +- CVE-2019-8457 - upgrade base image to python:3.11.5-slim-bookworm + +## [1.3.0] - 2023-05-19 +### Changed +- Adding IMAGE_NAME parameter +- Adding example diff --git a/graduated/gitops-argocd-sync/Dockerfile b/graduated/gitops-argocd-sync/Dockerfile new file mode 100644 index 000000000..65426bec2 --- /dev/null +++ b/graduated/gitops-argocd-sync/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.13.0a5-bookworm + + +# USER codefresh +RUN useradd -d /home/codefresh -m -s /usr/bin/bash codefresh +USER codefresh +WORKDIR /home/codefresh + +ENV PYTHONPATH /home/codefresh/.local/lib/python3.13/site-packages/ + +COPY --chown=codefresh requirements.txt requirements.txt +RUN pip3 install --upgrade pip +RUN pip3 install --user -r requirements.txt +COPY queries queries/ +COPY argocd_sync.py run.py +CMD [ "python3", "run.py"] diff --git a/graduated/gitops-argocd-sync/README.md b/graduated/gitops-argocd-sync/README.md new file mode 100644 index 000000000..a39b2abc4 --- /dev/null +++ b/graduated/gitops-argocd-sync/README.md @@ -0,0 +1,30 @@ +# gitops-argocd-sync + +Syncs Argo CD apps managed by our GitOps Runtimes using Codefresh API + +## Installation + +* `pip3 install -r requirements.txt` + +## Run + +* `python3 argocd_sync.py` + +## Supported parameters +| Name | Description | Optional | Default value | +|:---------------|:------------------------------------------------------------------------------------------|:---------|:--------------------------------------------| +| RUNTIME | The name of the GitOps Runtime managing the Argo CD Application | false | | +| APPLICATION | The name of the Argo CD Application to be synced | false | | +| ROLLBACK | Initiate a rollback to the previous revision if the Sync and Wait does not become healthy | true | | +| WAIT_ROLLBACK | Wait for the app to be healthy after a rollback. Forces ROLLBACK to true | true | | +| CA_BUNDLE | A base64 encoded string that contain the complete CA Certificate Bundle | true | | +| INSECURE | Allows the usage of a self-signed certificate in the chain to reach the API endpoint | true | | +| WAIT_HEALTHY | Wait for the app to be healthy | true | false | +| INTERVAL | Interval in seconds to wait between checks | true | 10 | +| MAX_CHECKS | Maximum numbers of checks to do (to avoid forever wait) | true | 10 | +| LOG_LEVEL | Set the log level, e.g. 'debug', 'info', 'warn', 'error', 'critical' (default 'error') | true | error | +| CF_URL | Codefresh API URL | true | https://g.codefresh.io | +| CF_API_KEY | Codefresh API token | true | | +| CF_STEP_NAME | Used in generating a link to the Apps Dashboard | true | STEP_NAME | +| IMAGE_NAME | Overwrites the image name | true | quay.io/codefreshplugins/gitops-argocd-sync | +| IMAGE_TAG | Overwrites the tag | true | 1.4.4 | diff --git a/graduated/gitops-argocd-sync/argocd_sync.py b/graduated/gitops-argocd-sync/argocd_sync.py new file mode 100644 index 000000000..c34e3a583 --- /dev/null +++ b/graduated/gitops-argocd-sync/argocd_sync.py @@ -0,0 +1,382 @@ +from gql import Client, gql +from gql.transport.requests import RequestsHTTPTransport +from gql.transport.exceptions import TransportQueryError +import os +import logging +import time +import sys +import json +import re + +PAGE_SIZE = 10 + +RUNTIME = os.getenv('RUNTIME') +APPLICATION = os.getenv('APPLICATION') + +# Wait and Rollback options +WAIT_HEALTHY = True if os.getenv('WAIT_HEALTHY', "false").lower() == "true" else False +INTERVAL = int(os.getenv('INTERVAL')) +MAX_CHECKS = int(os.getenv('MAX_CHECKS')) + +WAIT_ROLLBACK = True if os.getenv('WAIT_ROLLBACK', "false").lower() == "true" else False +ROLLBACK = True if os.getenv('ROLLBACK', "false").lower() == "true" else False +if WAIT_ROLLBACK: ROLLBACK = True + +CF_URL = os.getenv('CF_URL', 'https://g.codefresh.io') +CF_API_KEY = os.getenv('CF_API_KEY') +CF_STEP_NAME = os.getenv('CF_STEP_NAME', 'STEP_NAME') +LOG_LEVEL = os.getenv('LOG_LEVEL', "error") + +# Check the certificate or not accessing the API endpoint +VERIFY = True if os.getenv('INSECURE', "False").lower() == "false" else False +CA_BUNDLE = os.getenv('CA_BUNDLE') + +if CA_BUNDLE != None: + VERIFY='/root/bundle.pem' + +####################################################################### + + +def main(): + log_format = "%(asctime)s:%(levelname)s:%(name)s.%(funcName)s: %(message)s" + logging.basicConfig(format = log_format, level = LOG_LEVEL.upper()) + + logging.debug("RUNTIME: %s", RUNTIME) + logging.debug("APPLICATION: %s", APPLICATION) + logging.debug("WAIT: %s", WAIT_HEALTHY) + logging.debug("INTERVAL: %d", INTERVAL) + logging.debug("MAX CHECKS: %s", MAX_CHECKS) + logging.debug("ROLLBACK: %s", ROLLBACK) + logging.debug("VERIFY: %s", VERIFY) + logging.debug("BUNDLE: %s", CA_BUNDLE) + + ## Generating link to the Apps Dashboard + CF_OUTPUT_URL_VAR = CF_STEP_NAME + '_CF_OUTPUT_URL' + link_to_app = get_link_to_apps_dashboard() + export_variable(CF_OUTPUT_URL_VAR, link_to_app) + + ingress_host = get_runtime_ingress_host() + + # Does the app exist + # if not let's wait it has been recorded + # but not too long in case of simple misspelling + is_app_real=application_exist(ingress_host) + count=1 + while count <3 and is_app_real == False: + logging.debug("App does not exist yet %d", count) + time.sleep(INTERVAL) + count += 1 + is_app_real=application_exist(ingress_host) + + if application_exist(ingress_host) == False: + print(f"ERROR application {APPLICATION} does not seem to exist") + sys.exit(3) + + if application_autosync(ingress_host) == False: + execute_argocd_sync(ingress_host) + else: + logging.info("Skipping synchronization as Application is in auto-sync mode") + + namespace = get_runtime_ns() + health, sync = get_app_status(ingress_host) + + if WAIT_HEALTHY: + health, sync = waitHealthy (ingress_host) + + # if Wait failed, it's time for rollback + # Failed: Not healthy or out of sync + if ((health != "HEALTHY") or (sync == 'OUT_OF_SYNC')) and ROLLBACK: + logging.info("Application '%s' did not sync properly. Initiating rollback ", APPLICATION) + revision = getRevision(namespace) + logging.info("Latest healthy revision is %d", revision) + + rollback(ingress_host, namespace, revision) + + if WAIT_ROLLBACK: + logging.info("Waiting for rollback to happen") + health, sync = waitHealthy (ingress_host) + else: + time.sleep(INTERVAL) + health, sync = get_app_status(ingress_host) + else: + export_variable('ROLLBACK_EXECUTED', "false") + + # + # We care about those only if we want a HEALTH app + # + if health != "HEALTHY": + logging.error("Health Status is not HEALTHY. Exiting with error.") + sys.exit(1) + if sync == 'OUT_OF_SYNC': + logging.error("Sync Status is OUT OF SYNC. Exiting with error.") + sys.exit(1) + else: + export_variable('ROLLBACK_EXECUTED', "false") + + export_variable('HEALTH_STATUS', health) + + +####################################################################### + +def getRevision(namespace): + logging.debug ("Entering getRevision(%s)", namespace) + ## Get the latest healthy release + gql_api_endpoint = CF_URL + '/2.0/api/graphql' + transport = RequestsHTTPTransport( + url=gql_api_endpoint, + headers={'authorization': CF_API_KEY}, + verify=VERIFY, + retries=3, + ) + client = Client(transport=transport, fetch_schema_from_transport=False) + query = get_query('getReleases') ## gets gql query + variables = { + "filters": { + "namespace": namespace, + "runtime": RUNTIME, + "name": APPLICATION + }, + "pagination": { + "first": PAGE_SIZE + } + } + result = client.execute(query, variable_values=variables) + logging.debug("getRevision result: %s", result) + + loop=0 + revision = -1 + for edge in result['gitopsReleases']['edges']: + revision=edge['node']['argoHistoryId'] + health=edge['node']['application']['status']['healthStatus'] + + logging.debug("\nEdge %d\n current:%s\n revision: %d\n health: %s", + loop, edge['node']['current'], revision, health) + if (health == "HEALTHY"): + logging.info("Revision %d is HEALTHY", revision) + return revision + loop += 1 + # we did not find a HEALTHY one in our page + export_variable('ROLLBACK_EXECUTED', "false") + logging.error("Did not find a HEALTHY release among the last %d", PAGE_SIZE) + sys.exit(1) + +def waitHealthy (ingress_host): + logging.debug ("Entering waitHealthy (host: %s)", ingress_host) + + time.sleep(INTERVAL) + health, sync = get_app_status(ingress_host) + logging.info("App health: %s and sync: %s", health, sync) + loop=0 + while ((health != "HEALTHY") or (sync == 'OUT_OF_SYNC')) and loop < MAX_CHECKS: + logging.info("App health: %s and sync: %s after %d checks", health, sync, loop) + time.sleep(INTERVAL) + health, sync=get_app_status(ingress_host) + loop += 1 + + logging.debug ("Returning waitHealthy with health: '%s' and sync: '%s'", health, sync) + return health, sync + +def rollback(ingress_host, namespace, revision): + logging.debug ("Entering rollback(%s, %s, %s)", ingress_host, namespace, revision) + runtime_api_endpoint = ingress_host + '/app-proxy/api/graphql' + transport = RequestsHTTPTransport( + url=runtime_api_endpoint, + headers={'authorization': CF_API_KEY}, + verify=VERIFY, + retries=3, + ) + client = Client(transport=transport, fetch_schema_from_transport=False) + query = get_query('rollback') ## gets gql query + variables = { + "appName": APPLICATION, + "appNamespace": namespace, + "historyId": revision, + "dryRun": False, + "prune": True + } + logging.debug("Rollback variables: %s", variables) + result = client.execute(query, variable_values=variables) + logging.debug("Rollback result: %s", result) + export_variable('ROLLBACK_EXECUTED', "true") + + +def get_app_status(ingress_host): + ## Get the health and sync status of the app + # Health: HEALTHY, PROGRESSING + # Sync: OUT_OF_SYNC, SYNCED + + gql_api_endpoint = ingress_host + '/app-proxy/api/graphql' + transport = RequestsHTTPTransport( + url=gql_api_endpoint, + headers={'authorization': CF_API_KEY}, + verify=VERIFY, + retries=3, + ) + client = Client(transport=transport, fetch_schema_from_transport=False) + query = get_query('get_app_status') ## gets gql query + variables = { + "name": APPLICATION + } + result = client.execute(query, variable_values=variables) + + logging.debug("App Status result: %s", result) + health = result['applicationProxyQuery']['status']['health']['status'] + sync = result['applicationProxyQuery']['status']['sync']['status'] + return health, sync + +def get_query(query_name): + ## To do: get query content from a variable, failback to a file + with open('queries/'+query_name+'.graphql', 'r') as file: + query_content = file.read() + return gql(query_content) + + +def get_runtime(): + transport = RequestsHTTPTransport( + url = CF_URL + '/2.0/api/graphql', + headers={'authorization': CF_API_KEY}, + verify=VERIFY, + retries=3, + ) + client = Client(transport=transport, fetch_schema_from_transport=False) + query = get_query('getRuntime') ## gets gql query + variables = { + "runtime": RUNTIME + } + runtime = client.execute(query, variable_values=variables) + return runtime + + +def get_runtime_ingress_host(): + ingress_host = None + runtime = get_runtime() + ingress_host = runtime['runtime']['ingressHost'] + return ingress_host + + +def get_link_to_apps_dashboard(): + runtime = get_runtime() + runtime_ns = runtime['runtime']['metadata']['namespace'] + url_to_app = CF_URL+'/2.0/applications-dashboard/'+ runtime_ns +'/'+ RUNTIME +'/'+APPLICATION+'/timeline' + return url_to_app + +def get_runtime_ns(): + runtime = get_runtime() + runtime_ns = runtime['runtime']['metadata']['namespace'] + logging.debug("Runtime Namespace: %s", runtime_ns) + return runtime_ns + +def execute_argocd_sync(ingress_host): + runtime_api_endpoint = ingress_host + '/app-proxy/api/graphql' + transport = RequestsHTTPTransport( + url=runtime_api_endpoint, + headers={'authorization': CF_API_KEY}, + verify=VERIFY, + retries=3, + ) + client = Client(transport=transport, fetch_schema_from_transport=False) + query = get_query('argocd_sync') ## gets gql query + variables = { + "applicationName": APPLICATION, + "options": { + "prune": True + } + } + try: + result = client.execute(query, variable_values=variables) + except TransportQueryError as err: + if "NOT_FOUND_ERROR" in str(err): + print(f"ERROR: Application {APPLICATION} does not exist") + else: + print(f"ERROR: cannot sync Application {APPLICATION}") + logging.debug("Syncing App result: %s", err) + sys.exit(2) + except Exception as err: + print(f"ERROR: cannot sync Application {APPLICATION}") + logging.debug("Syncing App result: %s", err) + sys.exit(1) + +# +# Check for application existence +# if it does not exist, it will return 403 error +# +# Return True or False +# +def application_exist(ingress_host): + runtime_api_endpoint = ingress_host + '/app-proxy/api/graphql' + transport = RequestsHTTPTransport( + url=runtime_api_endpoint, + headers={'authorization': CF_API_KEY}, + verify=VERIFY, + retries=3, + ) + client = Client(transport=transport, fetch_schema_from_transport=False) + query = get_query('get_app_existence') ## gets gql query + variables = { + "applicationName": APPLICATION + } + try: + result = client.execute(query, variable_values=variables) + except TransportQueryError as err: + data = json.loads(re.sub('\'','\"', str(err))) + if (data["message"] == "Forbidden") and (data["extensions"] == 403): + return False + else: + print(f"ERROR: cannot test Application {APPLICATION}") + logging.error("Existence App result: %s", err) + sys.exit(1) + except Exception as err: + print(f"ERROR: cannot test Application {APPLICATION}") + logging.error("Existence App result: %s", err) + sys.exit(1) + return True + +# +# Check if app is in auto-sync mode +# +# Return True or False +# +def application_autosync(ingress_host): + runtime_api_endpoint = ingress_host + '/app-proxy/api/graphql' + transport = RequestsHTTPTransport( + url=runtime_api_endpoint, + headers={'authorization': CF_API_KEY}, + verify=VERIFY, + retries=3, + ) + client = Client(transport=transport, fetch_schema_from_transport=False) + query = get_query('get_app_autosync') ## gets gql query + variables = { + "applicationName": APPLICATION + } + try: + result = client.execute(query, variable_values=variables) + except Exception as err: + print(f"ERROR: cannot get sync policy from Application {APPLICATION}") + logging.debug("Application Sync policy result: %s", err) + sys.exit(1) + + logging.debug("App sync Policy: ", result['applicationProxyQuery']['spec']['syncPolicy']['automated']) + if result['applicationProxyQuery']['spec']['syncPolicy']['automated'] == None: + return False + else: + return True + + + +def export_variable(var_name, var_value): + path = os.getenv('CF_VOLUME_PATH') if os.getenv('CF_VOLUME_PATH') != None else './' + with open(path+'/env_vars_to_export', 'a') as a_writer: + a_writer.write(var_name + "=" + var_value+'\n') + + if os.getenv('CF_BUILD_ID') != None: + if os.getenv('CF_VOLUME_PATH') == None: os.mkdir('/meta') + with open('/meta/env_vars_to_export', 'a') as a_writer: + a_writer.write(var_name + "=" + var_value+'\n') + + logging.debug("Exporting variable: %s=%s", var_name, var_value) + +############################################################## + +if __name__ == "__main__": + main() diff --git a/graduated/gitops-argocd-sync/queries/argocd_sync.graphql b/graduated/gitops-argocd-sync/queries/argocd_sync.graphql new file mode 100644 index 000000000..91538460a --- /dev/null +++ b/graduated/gitops-argocd-sync/queries/argocd_sync.graphql @@ -0,0 +1,9 @@ +query sync($applicationName: String!, $options: SyncOptions) { + sync(applicationName: $applicationName, options: $options) { + metadata { + name + __typename + } + __typename + } +} \ No newline at end of file diff --git a/graduated/gitops-argocd-sync/queries/getReleases.graphql b/graduated/gitops-argocd-sync/queries/getReleases.graphql new file mode 100644 index 000000000..77039d2c4 --- /dev/null +++ b/graduated/gitops-argocd-sync/queries/getReleases.graphql @@ -0,0 +1,26 @@ +query ApplicationTimelineListQuery( + $filters: GitopsReleaseFilterArgs + $pagination: SlicePaginationArgs + $sort: GitopsReleaseSortArg +) { + gitopsReleases(pagination: $pagination, filters: $filters, sort: $sort) { + edges { + node { + current + argoHistoryId + application { + status { + syncStatus + healthStatus + } + } + } + } + pageInfo { + hasNextPage + hasPrevPage + startCursor + endCursor + } + } +} diff --git a/graduated/gitops-argocd-sync/queries/getRuntime.graphql b/graduated/gitops-argocd-sync/queries/getRuntime.graphql new file mode 100644 index 000000000..b2929e44b --- /dev/null +++ b/graduated/gitops-argocd-sync/queries/getRuntime.graphql @@ -0,0 +1,10 @@ +query getRuntime( + $runtime: String!) { + runtime(name: $runtime) { + metadata { + name + namespace + } + ingressHost + } +} \ No newline at end of file diff --git a/graduated/gitops-argocd-sync/queries/get_app_autosync.graphql b/graduated/gitops-argocd-sync/queries/get_app_autosync.graphql new file mode 100644 index 000000000..df21f0180 --- /dev/null +++ b/graduated/gitops-argocd-sync/queries/get_app_autosync.graphql @@ -0,0 +1,17 @@ +query appsyncstatus ($applicationName: String!) { + +applicationProxyQuery( + name: $applicationName + ){ + metadata { + name + } + spec { + syncPolicy { + automated { + prune + } + } + } + } +} diff --git a/graduated/gitops-argocd-sync/queries/get_app_existence.graphql b/graduated/gitops-argocd-sync/queries/get_app_existence.graphql new file mode 100644 index 000000000..dd173051e --- /dev/null +++ b/graduated/gitops-argocd-sync/queries/get_app_existence.graphql @@ -0,0 +1,9 @@ +query appexistence ($applicationName: String!) { + applicationProxyQuery( + name: $applicationName + ){ + metadata { + name + } + } +} diff --git a/graduated/gitops-argocd-sync/queries/get_app_status.graphql b/graduated/gitops-argocd-sync/queries/get_app_status.graphql new file mode 100644 index 000000000..574b0d645 --- /dev/null +++ b/graduated/gitops-argocd-sync/queries/get_app_status.graphql @@ -0,0 +1,17 @@ +query appstatus ($name: String!) { + applicationProxyQuery( + name: $name + ){ + metadata { + name + } + status { + health { + status + } + sync { + status + } + } + } +} diff --git a/graduated/gitops-argocd-sync/queries/get_app_status.orig.graphql b/graduated/gitops-argocd-sync/queries/get_app_status.orig.graphql new file mode 100644 index 000000000..796f42dd4 --- /dev/null +++ b/graduated/gitops-argocd-sync/queries/get_app_status.orig.graphql @@ -0,0 +1,18 @@ +query ApplicationsStatusesQuery( + $runtime: String! + $name: String! + $namespace: String +) { + application(runtime: $runtime, name: $name, namespace: $namespace) { + metadata { + runtime + name + namespace + cluster + __typename + } + healthStatus + syncStatus + syncPolicy + } +} diff --git a/graduated/gitops-argocd-sync/queries/rollback.graphql b/graduated/gitops-argocd-sync/queries/rollback.graphql new file mode 100644 index 000000000..62dafeaa9 --- /dev/null +++ b/graduated/gitops-argocd-sync/queries/rollback.graphql @@ -0,0 +1,15 @@ +query appRollback( + $appName: String! + $appNamespace: String! + $historyId: Int! + $dryRun: Boolean + $prune: Boolean +) { + appRollback( + appName: $appName + appNamespace: $appNamespace + historyId: $historyId + dryRun: $dryRun + prune: $prune + ) +} diff --git a/graduated/gitops-argocd-sync/requirements.txt b/graduated/gitops-argocd-sync/requirements.txt new file mode 100644 index 000000000..ca8a59832 --- /dev/null +++ b/graduated/gitops-argocd-sync/requirements.txt @@ -0,0 +1,11 @@ +backoff==2.2.1 +certifi==2024.2.2 +charset-normalizer==3.3.2 +gql==3.5.0 +graphql-core==3.2.3 +idna==3.6 +multidict==6.0.5 +requests==2.31.0 +requests-toolbelt==1.0.0 +urllib3==2.2.1 +yarl==1.9.4 diff --git a/graduated/gitops-argocd-sync/step.yaml b/graduated/gitops-argocd-sync/step.yaml new file mode 100644 index 000000000..0ded3d0d4 --- /dev/null +++ b/graduated/gitops-argocd-sync/step.yaml @@ -0,0 +1,165 @@ +kind: step-type +metadata: + name: gitops-argocd-sync + version: 1.4.5 + isPublic: true + description: Syncs Argo CD apps managed by our GitOps Runtimes + sources: + - 'https://github.com/codefresh-io/steps/tree/master/graduated/gitops-argocd-sync' + stage: incubating + maintainers: + - name: Francisco Cocozza + email: francisco@codefresh.io + - name: Laurent Rochette + email: laurent.rochette@codefresh.io + categories: + - GitOps + official: true + tags: [] + icon: + type: svg + url: https://cdn.jsdelivr.net/gh/codefresh-io/steps/graduated/argocd-sync/argo.svg + background: "#f4f4f4" + examples: + - description: Sync an ArgoCD app in a GitOps Runtime + workflow: + sync_argo_cd_app: + title: Sync Argo CD app + type: gitops-argocd-sync + arguments: + RUNTIME: my-runtime + APPLICATION: my-app + - description: Sync and wait for it to healthy. + workflow: + sync_argo_cd_app: + title: Sync Argo CD app + type: gitops-argocd-sync + arguments: + RUNTIME: my-runtime + APPLICATION: my-app + WAIT_HEALTHY: true + INTERVAL: 30 + MAX_CHECKS: 5 + - description: Sync, wait and rollback. + workflow: + sync_argo_cd_app: + title: Sync Argo CD app + type: gitops-argocd-sync + arguments: + RUNTIME: my-runtime + APPLICATION: my-app + WAIT_HEALTHY: true + INTERVAL: 60 + MAX_CHECKS: 3 + ROLLBACK: true + LOG_LEVEL: debug +spec: + arguments: |- + { + "definitions": {}, + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "patterns": [], + "required": [ + "RUNTIME", + "APPLICATION" + ], + "properties": { + "RUNTIME": { + "type": "string", + "description": "The name of the GitOps Runtime managing the Argo CD Application" + }, + "APPLICATION": { + "type": "string", + "description": "The name of the Argo CD Application to be synced" + }, + "WAIT_HEALTHY": { + "type": "boolean", + "description": "OPTIONAL - Wait for the app to be healthy", + "default": false + }, + "INTERVAL": { + "type": "integer", + "description": "OPTIONAL - Interval in seconds to wait between checks", + "default": 10 + }, + "MAX_CHECKS": { + "type": "integer", + "description": "OPTIONAL - Maximum numbers of checks to do (to avoid forever wait). Default is 10.", + "default": 10 + }, + "ROLLBACK": { + "type": "boolean", + "description": "OPTIONAL - Initiate a rollback to the previous revision if the Sync and Wait does not become healthy", + "default": false + }, + "WAIT_ROLLBACK": { + "type": "boolean", + "description": "OPTIONAL - Wait for the app to be healthy after a rollback. Forces ROLLBACK to true", + "default": false + }, + "CA_BUNDLE": { + "type": "string", + "description": "OPTIONAL - a base64 encoded string that contain the complete CA Certificate Bundle" + }, + "INSECURE": { + "type": "boolean", + "description": "OPTIONAL - to allow the usage of a self-signed certificate in the chain to reach the API endpoint", + "default": false + }, + "LOG_LEVEL": { + "type": "string", + "description": "OPTIONAL - set the log level, e.g. 'debug', 'info', 'warn', 'error', 'critical' (default 'error')", + "default": "error" + }, + "IMAGE_NAME": { + "type": "string", + "default": "quay.io/codefreshplugins/gitops-argocd-sync", + "description": "OPTIONAL - To overwrite the image name to use" + }, + "IMAGE_TAG": { + "type": "string", + "default": "1.4.5", + "description": "OPTIONAL - To overwrite the tag to use" + } + } + } + returns: |- + { + "definitions": {}, + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": true, + "patterns": [], + "required": [ + "HEALTH_STATUS" + ], + "properties": { + "HEALTH_STATUS": { + "type": "string", + "description": "The health status of the application: HEALTHY, PROGRESSING, DEGRADED or SUSPENDED" + }, + "ROLLBACK_EXECUTED": { + "type": "string", + "description": "A flag to indicate if a rollback was executed or not" + } + } + } + stepsTemplate: |- + argo_cd_sync: + image: '[[.Arguments.IMAGE_NAME]]:[[.Arguments.IMAGE_TAG]]' + environment: + [[ range $key, $val := .Arguments ]] + - '[[ $key ]]=[[ $val ]]' + [[- end ]] + commands: + [[ if .Arguments.CA_BUNDLE ]] + - echo [[ .Arguments.CA_BUNDLE ]] | base64 -d >/root/bundle.pem + [[ end ]] + - cd /home/codefresh + - python3 run.py + + delimiters: + left: '[[' + right: ']]' diff --git a/incubating/argo-cd-sync/README.md b/incubating/argo-cd-sync/README.md index 0d9809cdf..fef72bca7 100644 --- a/incubating/argo-cd-sync/README.md +++ b/incubating/argo-cd-sync/README.md @@ -1,5 +1,8 @@ # gitops-argocd-sync +> [!WARNING] +> The step is deprecated. Use [gitops-argocd-sync](https://github.com/codefresh-io/steps/blob/master/graduated/gitops-argocd-sync) instead. + Syncs Argo CD apps managed by our GitOps Runtimes using Codefresh API ## Installation diff --git a/incubating/argo-cd-sync/step.yaml b/incubating/argo-cd-sync/step.yaml index 79bd5cb67..e9224cef9 100644 --- a/incubating/argo-cd-sync/step.yaml +++ b/incubating/argo-cd-sync/step.yaml @@ -3,7 +3,7 @@ metadata: name: argo-cd-sync version: 1.4.5 isPublic: true - description: Syncs Argo CD apps managed by our GitOps Runtimes + description: The step is deprecated. Use gitops-argocd-sync instead. Syncs Argo CD apps managed by our GitOps Runtimes sources: - 'https://github.com/codefresh-io/steps/tree/master/incubating/argo-cd-sync' stage: incubating