GithubHelp home page GithubHelp logo

argoproj-labs / gordian Goto Github PK

View Code? Open in Web Editor NEW
78.0 8.0 22.0 134 KB

Gordian applies transformations to files across multiple github repositories and creates pull requests for the owners of the repositories to review and merge them.

License: Apache License 2.0

Dockerfile 1.19% Python 98.23% Shell 0.59%

gordian's Introduction

Gordian

Latest Release Latest PyPi Version codecov Python Build Status Docker Build Status

Gordian applies transformations to files in github repositories and create PRs for the owners of the repositories to review and merge them.

This project grew from a need to keep various kubernetes services consistent and roll out changes at scale. The main use case for this tool is to make changes to configuration files across multiple repositories simultaneously.

Usage

Search and Replace

You can use the docker image to search and replace various strings across repositories. It supports simple and complex search/replace strings operations.

docker run --rm -it argoprojlabs/gordian:latest -h
usage: gordian [-h] [-c CONFIG_FILE] [-g GITHUB_API] --pr PR_MESSAGE [-v] [-d]
               [-b BRANCH] [-t TARGET_BRANCH] [-l PR_LABELS [PR_LABELS ...]]
               [-M | -m | -p]
               [--description DESCRIPTION | --description-file DESCRIPTION_FILE]
               [--force-changelog FORCE_CHANGELOG] -s SEARCH -r REPLACE

optional arguments:
  -h, --help            show this help message and exit
  -c CONFIG_FILE, --config CONFIG_FILE
                        Config file path. (default: config.yaml)
  -g GITHUB_API, --github-api GITHUB_API
                        Github API URL (default: None)
  --pr PR_MESSAGE       Pull request name. (default: None)
  -v, --verbose
  -d, --dry-run         Enable dry run mode (default: False)
  -b BRANCH, --branch BRANCH
                        Branch name to use (default: None)
  -t TARGET_BRANCH, --target-branch TARGET_BRANCH
                        Target branch (default: master)
  -l PR_LABELS [PR_LABELS ...], --labels PR_LABELS [PR_LABELS ...]
                        List of space separated label names you wish to add to
                        your pull request(s) (default: [])
  -F FILE, --file FILE
                        File to change, currently only supported with
                        PlainTextUpdater. (default: None)
  -M, --major           Bump the major version. (default: None)
  -m, --minor           Bump the minor version. (default: None)
  -p, --patch           Bump the patch version. (default: None)
  --description DESCRIPTION
                        Description to be passed to the PR. (default: )
  --description-file DESCRIPTION_FILE
                        Local file path for the description to be passed to
                        the PR. (default: None)
  --force-changelog FORCE_CHANGELOG
                        Fail if changelog does not exist or cannot be parsed
                        (default: None)
  -s SEARCH, --search SEARCH
                        The string to search for in config files. (default:
                        None)
  -r REPLACE, --replace REPLACE
                        The string that will replace instances of the searched
                        string. (default: None)

Simple transformations

You can use the command line interface to make simple changes across various JSON and YAML files, as shown in this example that modifies a kubernetes API Version.

The following command will update the repositories listed in the default config file config.yaml by making a new pull request to the target master branch using a new origin branch update_k8s_apiversion by updating all files that contains apiVersion: apps/v1beta2 to replace it with apiVersion: apps/v1. The new PR will have the labels k8s.1.16 and support assigned to it and the minor version will be bumped.

docker run --rm -it argoprojlabs/gordian:latest -b "update_k8s_apiversion" --pr "update_k8s_apiversion" -s "apiVersion: apps/v1beta2" -r "apiVersion: apps/v1" -l k8s.1.16 -v -m

Complex transformations

You can use the python script interface to make complex changes across various JSON and YAML files, as shown in this example that modifies a kubernetes resource. You can see more examples in the examples directory.

import sys
from gordian.gordian import get_basic_parser, apply_transformations
from gordian.transformations import Transformation

class PreScale(Transformation):

    def __init__(self, args, repo):
        super().__init__(args, repo)
        self.environments = args.environments

    def run(self):
        for env in self.environments:
            objects = self.repo.get_objects(f'overlays/{env}/envconfig-values.yaml')

            min_replicas = None
            for obj in objects:
                if obj['kind'] != 'HorizontalPodAutoscaler':
                    continue

                if obj['spec']['minReplicas'] != obj['spec']['maxReplicas']:
                    min_replicas = obj['spec']['minReplicas']
                    obj['spec']['maxReplicas'] = min_replicas

            if min_replicas is not None:
                objects.save(f'Setting maxRelicas = minReplicas = {min_replicas}', self.dry_run)

        self.repo.changelog.added('Set max replicas equal to min replicas', 'TICKET-1234')
        self.repo.changelog.save('Update changelog', self.dry_run)

if __name__ == '__main__':
    parser = get_basic_parser()
    parser.add_argument(
        '-e', '--environments',
        required=False,
        dest='environments',
        default=['prd'],
        action='append',
        help='Environments to update.'
    )
    args = parser.parse_args(sys.argv[1:])
    apply_transformations(args, [PreScale])

Dependencies

  • config.yaml (required) - list of repositories you wish to modify
  • GIT_USERNAME (optional) - your Github username
  • GIT_PASSWORD (optional) - your Github password or Personal Access Token
  • GIT_TOKEN (optional) - Github Personal Access Token that grants write access to the specified repositories

Authentication

Two methods of authentication are available:

  • Using a Personal Access Token
  • Using a Github Username & Password

A Github Personal Access Token, Github Username and Github Password can also be passed in via the token=, username= and password= named parameters. The passed value will always take precedence over any environment variable. (Added in 3.5.0)

Authentication - Personal Access Token

A Personal Access Token can be used in two ways:

  • Setting the GIT_TOKEN environment variable
  • Passing the token= named parameter

The Personal Access Token must have write access to any specified repositories you wish to submit changes to.

Authentication - Github Username & Password

A Github Username and Password combination can be used in two ways:

  • Setting the GIT_USERNAME and GIT_PASSWORD environment variables
  • Passing the username= and password= parameters

The user must have write access to any specified repositories you wish to submit changes to.

The GIT_PASSWORD or password= may also contain a Personal Access Token instead of the account password.

Development

The simplest way to hit the ground running if you want to contribute with code is using docker.

Launch a python container

localhost$ docker run --rm -it  -v $(pwd):$(pwd) -w $(pwd)  python:3.11-slim-bullseye bash

Install the project and test dependencies in developer mode

container# pip install -e .[test]

Run the tests

container# pytest
=========================================== test session starts ============================================
platform linux -- Python 3.7.1, pytest-4.5.0, py-1.8.0, pluggy-0.11.0
rootdir: /Users/user/git/argoproj-labs
plugins: requests-mock-1.6.0, cov-2.7.1
collected 33 items

....
================================== 33 passed, 2 warnings in 1.73 seconds ===================================

Support

Creators

Contributors

gordian's People

Contributors

agarfu avatar anshulv98 avatar bgreg1012 avatar coreycaverly avatar foutoucour avatar jremond avatar kaosx5s avatar karstensiemer avatar sharpyy avatar zejunintuit avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gordian's Issues

Process is failing if branch is provided

I'm running the following command:

python3 chart-updater.py -c gordian.yaml -b argocd-chart-update --pr update_chart_version 

I'm getting the following error message:

[2020-10-28 19:43:28,353] INFO Processing repo: hipagesgroup/salesforce-syncer
Traceback (most recent call last):
  File "chart-updater.py", line 84, in <module>
    main()
  File "chart-updater.py", line 81, in main
    apply_transformations(args, [ChartUpdater])
  File "/usr/local/lib/python3.8/site-packages/gordian/gordian.py", line 167, in apply_transformations
    transform(args, transformations, config.get_data(), pr_description, pr_created_callback=pr_created_callback)
  File "/usr/local/lib/python3.8/site-packages/gordian/gordian.py", line 188, in transform
    transformation(args, repo).run()
  File "chart-updater.py", line 20, in run
    file = self.repo.find_file(f'.argocd.yml')
  File "/usr/local/lib/python3.8/site-packages/gordian/repo.py", line 92, in find_file
    for file in self.get_files():
  File "/usr/local/lib/python3.8/site-packages/gordian/repo.py", line 74, in get_files
    contents = self._get_repo_contents('')
  File "<decorator-gen-2>", line 2, in _get_repo_contents
  File "/usr/local/lib/python3.8/site-packages/retry/api.py", line 73, in retry_decorator
    return __retry_internal(partial(f, *args, **kwargs), exceptions, tries, delay, max_delay, backoff, jitter,
  File "/usr/local/lib/python3.8/site-packages/retry/api.py", line 33, in __retry_internal
    return f()
  File "/usr/local/lib/python3.8/site-packages/gordian/repo.py", line 128, in _get_repo_contents
    raise e
  File "/usr/local/lib/python3.8/site-packages/gordian/repo.py", line 125, in _get_repo_contents
    return self._source_repo.get_contents(path, self.source_branch)
  File "/usr/local/lib/python3.8/site-packages/github/Repository.py", line 1752, in get_contents
    headers, data = self._requester.requestJsonAndCheck(
  File "/usr/local/lib/python3.8/site-packages/github/Requester.py", line 317, in requestJsonAndCheck
    return self.__check(
  File "/usr/local/lib/python3.8/site-packages/github/Requester.py", line 342, in __check
    raise self.__createException(status, responseHeaders, output)
github.GithubException.GithubException: 404 {"message": "No commit found for the ref refs/heads/argocd-chart-update", "documentation_url": "https://docs.github.com/v3/repos/contents/"}

GithubException when doing complex transformations

Hi Guys!
First of all thank you for your work on this project!

I already successfully created some PRs using the simple search and replace syntax. Forking and all that stuff working great.

Now I want to do complex transformations but fail.

This is my transformation script:

import sys
import time
from github import GithubException
from gordian.gordian import get_basic_parser, apply_transformations
from gordian.transformations import Transformation
from gordian.config import Config
from gordian.repo import Repo

class PreScale(Transformation):

    def __init__(self, args, repo):
        super().__init__(args, repo)
        self.environments = args.environments
        self.image = args.image
        self.image_tag = args.image_tag

    def run(self):
        for env in self.environments:
            objects = self.repo.get_objects(f'kustomizations/overlays/{env}/kustomization.yaml')

            for obj in objects:
                if obj['kind'] != 'Kustomization':
                    continue

                for img in obj['images']:
                    if img['name'] != self.image:
                        continue

                    img['newTag'] = self.image_tag

                for i in range (1, 4):
                    try:
                        objects.save(f'Setting {self.image} image tag to {self.image_tag}', self.dry_run)
                    except GithubException as e:
                        time.sleep(i)

if __name__ == '__main__':
    parser = get_basic_parser()
    parser.add_argument(
        '-e', '--environments',
        required=True,
        dest='environments',
        action='append',
        help='Environments to update.'
    )
    parser.add_argument(
        '-i', '--image',
        required=True,
        dest='image',
        action='store',
        help='image to update.'
    )
    parser.add_argument(
        '-a', '--image-tag',
        required=True,
        dest='image_tag',
        action='store',
        help='tag for the image to update.'
    )
    args = parser.parse_args(sys.argv[1:])
    apply_transformations(args, [PreScale])

I added the for loop with the sleeps because I was getting this error:

16:52:12  + python -u gordian/transformation.py --config gordian/destinationRepositories.yaml --github-api https://api.github.com --pr Update-sda-bash-utils-to-0.2.0-dev-202006290251 --branch sda-bash-utils-0.2.0-dev-202006290251 --target-branch dev --image quay.io/sdase/sda-bash-utils --image-tag 0.2.0-dev-202006290251 --environments dev --patch --verbose
16:52:12  [2020-06-29 14:52:12,397] gordian.transform INFO Processing repo: some-repo
16:52:12  [2020-06-29 14:52:12,397] repo.__init__ DEBUG Using git username and password
16:52:12  [2020-06-29 14:52:12,656] repo.__init__ DEBUG Github api url: https://api.github.com
16:52:12  [2020-06-29 14:52:12,656] repo.__init__ DEBUG Repo name: some-repo
16:52:12  [2020-06-29 14:52:12,656] repo.__init__ DEBUG Target ref: dev
16:52:12  [2020-06-29 14:52:12,656] repo.__init__ DEBUG Branch name for this changes: refs/heads/sda-bash-utils-0.2.0-dev-202006290251
16:52:12  [2020-06-29 14:52:12,656] repo.get_files DEBUG Getting repo content
16:52:25  [2020-06-29 14:52:24,823] repo._get_new_version INFO There is no version file in the repository, skipping bumping
16:52:25  [2020-06-29 14:52:25,020] repo.update_file INFO Setting quay.io/sdase/sda-bash-utils image tag to 0.2.0-dev-202006290251
16:52:25  [2020-06-29 14:52:25,020] repo._make_branch INFO Forking repo...
16:52:25  [2020-06-29 14:52:25,420] repo._get_branch DEBUG Fetching branch dev...
16:52:25  [2020-06-29 14:52:25,651] repo._make_branch DEBUG Creating branch refs/heads/sda-bash-utils-0.2.0-dev-202006290251
16:52:26  [2020-06-29 14:52:26,466] repo.update_file DEBUG Updating file kustomizations/overlays/dev/kustomization.yaml
16:52:26  Traceback (most recent call last):
16:52:26    File "gordian/transformation.py", line 32, in run
16:52:26      objects.save(f'Setting {self.image} image tag to {self.image_tag}', self.dry_run)
16:52:26    File "/usr/local/lib/python3.7/site-packages/gordian/files/base_file.py", line 26, in save
16:52:26      self.repo.update_file(self.github_file, self._dump(), message, dry_run)
16:52:26    File "/usr/local/lib/python3.7/site-packages/gordian/repo.py", line 158, in update_file
16:52:26      branch=self.branch_name
16:52:26    File "/usr/local/lib/python3.7/site-packages/github/Repository.py", line 2101, in update_file
16:52:26      input=put_parameters,
16:52:26    File "/usr/local/lib/python3.7/site-packages/github/Requester.py", line 319, in requestJsonAndCheck
16:52:26      verb, url, parameters, headers, input, self.__customConnection(url)
16:52:26    File "/usr/local/lib/python3.7/site-packages/github/Requester.py", line 342, in __check
16:52:26      raise self.__createException(status, responseHeaders, output)
16:52:26  github.GithubException.GithubException: 409 {"message": "kustomizations/overlays/dev/kustomization.yaml does not match 2741f1836ecb27558f61bd1e9246005cc415c7a2", "documentation_url": "https://developer.github.com/v3/repos/contents/#create-or-update-a-file"}

Because I thought it was doing the commit too fast after the fork.
Like discussed here: https://stackoverflow.com/questions/33666838/determine-if-a-fork-is-ready

With the for loop it still fails like this:

09:12:51  + python -u gordian/transformation.py --config gordian/destinationRepositories.yaml --github-api https://api.github.com --pr Update-sda-bash-utils-to-0.1.4-dev-202006300712 --branch sda-bash-utils-0.1.4-dev-202006300712 --target-branch dev --image quay.io/sdase/sda-bash-utils --image-tag 0.1.4-dev-202006300712 --environments dev --patch --verbose
09:12:51  [2020-06-30 07:12:51,854] gordian.transform INFO Processing repo: some-repo
09:12:51  [2020-06-30 07:12:51,855] repo.__init__ DEBUG Using git username and password
09:12:52  [2020-06-30 07:12:52,110] repo.__init__ DEBUG Github api url: https://api.github.com
09:12:52  [2020-06-30 07:12:52,110] repo.__init__ DEBUG Repo name: some-repo
09:12:52  [2020-06-30 07:12:52,110] repo.__init__ DEBUG Target ref: dev
09:12:52  [2020-06-30 07:12:52,110] repo.__init__ DEBUG Branch name for this changes: refs/heads/sda-bash-utils-0.1.4-dev-202006300712
09:12:52  [2020-06-30 07:12:52,110] repo.get_files DEBUG Getting repo content
09:13:07  [2020-06-30 07:13:05,321] repo._get_new_version INFO There is no version file in the repository, skipping bumping
09:13:07  [2020-06-30 07:13:05,660] repo.update_file INFO Setting quay.io/sdase/sda-bash-utils image tag to 0.1.4-dev-202006300712
09:13:07  [2020-06-30 07:13:05,661] repo._make_branch INFO Forking repo...
09:13:07  [2020-06-30 07:13:05,982] repo._get_branch DEBUG Fetching branch dev...
09:13:07  [2020-06-30 07:13:06,215] repo._make_branch DEBUG Creating branch refs/heads/sda-bash-utils-0.1.4-dev-202006300712
09:13:07  [2020-06-30 07:13:06,937] repo.update_file DEBUG Updating file kustomizations/overlays/dev/kustomization.yaml
09:13:08  [2020-06-30 07:13:08,183] repo.update_file INFO Setting quay.io/sdase/sda-bash-utils image tag to 0.1.4-dev-202006300712
09:13:08  [2020-06-30 07:13:08,183] repo.update_file DEBUG Updating file kustomizations/overlays/dev/kustomization.yaml
09:13:10  [2020-06-30 07:13:10,418] repo.update_file INFO Setting quay.io/sdase/sda-bash-utils image tag to 0.1.4-dev-202006300712
09:13:10  [2020-06-30 07:13:10,418] repo.update_file DEBUG Updating file kustomizations/overlays/dev/kustomization.yaml
09:13:14  [2020-06-30 07:13:14,048] gordian.transform INFO PR already exists for refs/heads/sda-bash-utils-0.1.4-dev-202006300712. Error: 422 {"message": "Validation Failed", "errors": [{"resource": "PullRequest", "code": "custom", "message": "No commits between SDA-SE:dev and ****:sda-bash-utils-0.1.4-dev-202006300712"}], "documentation_url": "https://developer.github.com/v3/pulls/#create-a-pull-request"}

Even though it says that a PR already exists, there was none created.
Guessing that the commit itself internally worked at first try and my loop is incorrect, because only the push fails and it tries to issue a commit again, which cannot have any differences.
Not sure how to solve it.

The file I am trying to transform looks like this
kustomizations/overlays/dev/kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

bases:
  - ../../base

configurations:
  - configurations.yaml

images:
- name: quay.io/sdase/sda-bash-utils
  newTag: "0.2.0-202006250718"
- name: someimage
  newTag: "sometag"

When I add print commands to the transformation script I can clearly see that the function itself is properly swapping the tags.

Version of gordian used is 2.1.2

Add SECURITY.md

The Argo maintainers recently agreed to require all Argoproj Labs project repositories to contain a SECURITY.md file which documents:

  • Contact information for reporting security vulnerabilities
  • Some minimal information about policies, practices, with possibly links to further documentation with more details

This will help direct vulnerability reporting to the right parties which can fix the issue.

You are free to use the following as examples/templates:

Also, please note that in the future we are exploring a requirement that argoproj-labs projects perform a CII self-assessment to better inform its users about which security best practices are being followed.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.