renovate icon indicating copy to clipboard operation
renovate copied to clipboard

Update doc for ecr and codeartifact

Open leoffj opened this issue 3 years ago • 5 comments

What would you like Renovate to be able to do?

In order to successfully self-host renovate to work with aws ecr for docker images and aws codeartifact for nuget/pypi/... quite some configuration effort is needed. I wanted to share my results so the documentation can be updated. Hopefully this will be helpful to someone =)

If you have any ideas on how this should be implemented, please tell us here.

First of all, codeartifact requires temporary credentials. I obtained those using an init-container. Here is my cron job:

kind: CronJob
metadata:
  name: renovate
  namespace: renovate
spec:
  schedule: '@daily'
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      backoffLimit: 0
      template:
        spec:
          serviceAccountName: renovate
          initContainers:
            - name: init-config
              image: <e.g. alpine with aws cli installed>
              # code artifact is only accessible via temporary credentials. 
              # Until https://github.com/renovatebot/renovate/issues/11325 is done, we need to obtain the token manually, and insert it into the config
              command:
                - /bin/bash
                - -c
                - |
                  #!/bin/bash
                  AUTH_TOKEN=$( aws codeartifact get-authorization-token --domain <domain> --domain-owner 123456789012 --query authorizationToken --output text )
                  sed "s/<PLACEHOLDER>/$AUTH_TOKEN/g" /workdir/config.json >> /processed/config.json
              volumeMounts:
                - name: config-volume
                  mountPath: /processed
                - name: template-volume
                  mountPath: /workdir
          containers:
            - name: renovate
              image: renovate/renovate:31.28
              env:
                - name: LOG_LEVEL
                  value: info
                - name: RENOVATE_CONFIG_FILE
                  value: "/usr/src/app/config/config.json"
              envFrom:
                - secretRef:
                    name: bitbucket-renovate-token
                - secretRef:
                    name: github-renovate-token
              volumeMounts:
                - name: config-volume
                  mountPath: /usr/src/app/config/
                  readOnly: true
          restartPolicy: Never
          volumes:
            - name: template-volume
              configMap:
                name: renovate-config
            - name: config-volume
              emptyDir: {}

This is my config map:

kind: ConfigMap
metadata:
  name: renovate-config
  namespace: renovate
data:
  config.json: |-
    {
      "dryRun"            : false,
      "autodiscover"      : true,
      "platform"          : "bitbucket-server",
      "endpoint"          : "https://git.my_company.com",
      "username"          : "<the bots name>",
      "gitAuthor"         : "<the bots name>",
      "packageRules": [
        {
          "matchDatasources": ["nuget"],
          "registryUrls": [
            "https://api.nuget.org/v3/index.json",
            "https://<domain>-123456789012.d.codeartifact.<region>.amazonaws.com/nuget/<nu-get-repo>/v3/index.json"
          ]
        }
      ],
      "hostRules"    : [
        {"matchHost":"https://123456789012.dkr.ecr.<region>.amazonaws.com", "enabled":true, "hostType":"docker"},
        {"matchHost":"<domain>-123456789012.d.codeartifact.<region>.amazonaws.com", "enabled":true, "token":"<PLACEHOLDER>"}
      ],
      "branchPrefix"       : "renovate-",
      "onboardingBranch"   : "renovate-configure"
    }

In addition, you need to create the namespace, service account and secrets obviously, as well as a iam role for the service account. I use the following iam role, which (in my opinion) has sufficiently minimalistic permissions:

    "Version" : "2012-10-17",
    "Statement" : [
      {
        "Sid" : "Stmt1642517217303",
        "Action" : [
          "ecr:BatchCheckLayerAvailability",
          "ecr:BatchGetImage",
          "ecr:DescribeImageReplicationStatus",
          "ecr:DescribeImageScanFindings",
          "ecr:DescribeImages",
          "ecr:DescribePullThroughCacheRules",
          "ecr:DescribeRegistry",
          "ecr:DescribeRepositories",
          "ecr:GetDownloadUrlForLayer",
          "ecr:GetLifecyclePolicy",
          "ecr:GetLifecyclePolicyPreview",
          "ecr:GetRegistryPolicy",
          "ecr:GetRegistryScanningConfiguration",
          "ecr:GetRepositoryPolicy",
          "ecr:ListImages",
          "ecr:ListTagsForResource"
        ],
        "Effect" : "Allow",
        "Resource" : "arn:aws:ecr:<region>:123456789012:repository/*"
      },
      {
        "Sid" : "Stmt1642517217304",
        "Action": ["ecr:GetAuthorizationToken"],
        "Effect": "Allow",
        "Resource": "*"        
      },
      {
        "Sid" : "Stmt1642517217305",
        "Action" : [
          "codeartifact:Get*",
          "codeartifact:List*",
          "codeartifact:Describe*",
          "codeartifact:Read*"
        ],
        "Effect" : "Allow",
        "Resource" : [
          "arn:aws:codeartifact:<region>:123456789012:domain/<domain>",
          "arn:aws:codeartifact:<region>:123456789012:repository/<domain>/*"
        ]
      },
      {
        "Sid" : "Stmt1642517217306",
        "Action" : "sts:GetServiceBearerToken",
        "Effect" : "Allow",
        "Resource" : "*",
        "Condition" : {
          "StringEquals" : {
            "sts:AWSServiceName" : "codeartifact.amazonaws.com"
          }
        }
      }
    ]
  }

Is this a feature you are interested in implementing yourself?

No

leoffj avatar Jan 26 '22 13:01 leoffj

Thanks. Such documentation can live in https://github.com/renovatebot/renovate/blob/main/lib/datasource/docker/readme.md where it would then end up in https://docs.renovatebot.com/modules/datasource/docker/

rarkins avatar Jan 26 '22 15:01 rarkins

Where do you log into ECR?

I'm expecting commands like this in your init somewhere, so helm and docker can auth.

aws ecr get-login-password --region us-west-2 > /tmp/creds

cep21 avatar Dec 20 '22 16:12 cep21

Renovate does not call helm or docker directly

rarkins avatar Dec 20 '22 17:12 rarkins

I'm increasing the priority of this issue. We're getting questions about AWS CodeArtifact in the discussions. We can probably prevent questions by updating the docs. 😄

HonkingGoose avatar Jan 05 '23 12:01 HonkingGoose

We got it working. I'm pasting a redacted helm values.yaml below of our helm chart with the important parts required. Some notes

  • We use github for NPM, AWS for PY and ECR, and some github for ECR
  • Use init containers to get both the github app token (for renovatebot) as well as the AWS CLI auth tokens
  • Github still requires a robot USER token (not app token) for NPM
  • Heavy use of the "init container to file, then read file to variable" pattern.
  • Use kubernetes external-secrets to load up our github app token and user tokens from ENV
    existingSecret: renovatebot-env
    serviceAccount:
      create: true
      annotations:
        eks.amazonaws.com/role-arn: arn:aws:iam::${cluster_account_id}:role/${cluster_env}-renovatebot
    image:
      repository: ${cluster_account_id}.dkr.ecr.us-west-2.amazonaws.com/third-party/renovate/renovate
    extraVolumes:
      - name: shared
        emptyDir: {}
      - name: renovatebot-apptoken
        secret:
          secretName: renovatebot-apptoken
      - name: renovatebot-extra-config
        configMap:
          name: renovatebot-extra-config
    extraVolumeMounts:
      - mountPath: /shared
        name: shared
      - name: renovatebot-extra-config
        mountPath: /usr/src/app/config.js
        subPath: config.js
    redis:
      # Cache run results
      enabled: true
    env:
      LOG_LEVEL: debug
    extraConfigmaps:
      - name: config
        data:
          # A lot of this idea is taken from https://github.com/renovatebot/helm-charts/discussions/237
          config.js: |
            const fs = require('fs');
            // fs read idea taken from https://github.com/renovatebot/helm-charts/issues/248
            const  token = fs.readFileSync('/shared/token', 'utf8');
            // Token from code-artifact
            const pipToken = fs.readFileSync('/shared/pip-login-token', 'utf8').trim();
            // All the configuration options are here: https://docs.renovatebot.com/self-hosted-configuration/#token
            module.exports = {
              // Without trim, the \n at the end of the file causes the token to be invalid
              token: token.trim(),
              // Some of these config options are from https://docs.renovatebot.com/modules/platform/github/
              // Note: must EXACTLY match application name or bad things happen
              username: 'our-renovatebot[bot]',
              gitAuthor: "our-bot <123456+our-bot[bot]@users.noreply.github.enterprise.com>",
              platform: "github",
              autodiscover: true,
              // Run everywhere!
              autodiscoverFilter: ["COMPANYH/*"],
              // https://docs.renovatebot.com/self-hosted-configuration/#onboardingconfigfilename
              onboardingConfigFileName: ".github/renovate.json",
              hostRules: [
                {
                  matchHost: 'https://ghcr.io',
                  token: token.trim()
                }, {
                  matchHost: "https://npm.pkg.github.com/",
                  hostType: "npm",
                  token: process.env.ROBOT_TOKEN
                }, {
                  matchHost: "COMPANY-private-123123123.d.codeartifact.us-west-2.amazonaws.com",
                  token:pipToken
                }
              ],
              python: {
                registryUrls: ["https://COMPANY-private-123123123.d.codeartifact.us-west-2.amazonaws.com/pypi/pypi-private/simple/"]
              },
              npmrc: "@COMPANY:registry=https://npm.pkg.github.com/",
              secrets: {}
            }
    cronjob:
      # Run every hour
      schedule: "0 * * * *"
      initContainers:
        - name: github-app-installation-token
          env:
            # Get these two values from https://github.com/organizations/COMPANY/settings/installations/123123
          - name: GITHUB_APP_ID
            value: "123123"
          - name: GITHUB_INSTALLATION_ID
            value: "456456"
          image: node:16.17.1-alpine3.15
          command: ["/bin/sh"]
          args:
            - -c
            - |
              npx github-app-installation-token \
                --appId $GITHUB_APP_ID \
                --installationId $GITHUB_INSTALLATION_ID \
                --privateKeyLocation /secret/renovatebot-apptoken/renovatebot-apptoken.pem > /shared/token
          volumeMounts:
            - mountPath: /shared
              name: shared
            - name: renovatebot-apptoken
              mountPath: "/secret/renovatebot-apptoken"
              readOnly: true
        - name: aws-cli-commands
          image: amazon/aws-cli:2.9.9
          command: ["/bin/bash"]
          args:
            - -c
            - |
              #!/bin/bash
              aws codeartifact get-authorization-token --domain COMPANY-private --domain-owner 123123123 \
                --query authorizationToken --output text > /shared/pip-login-token
          volumeMounts:
            - mountPath: /shared
              name: shared
    renovate:
      existingConfigFile: "/usr/src/app/config.js"

cep21 avatar Jan 09 '23 19:01 cep21

Can someone please share the detailed steps on how to do this? I have been trying to do the same since last 3 weeks but no luck yet. @leoffj @rarkins @cep21 @HonkingGoose

faisaldbank avatar Mar 31 '23 09:03 faisaldbank

I don't know enough about this to help you.

HonkingGoose avatar Mar 31 '23 09:03 HonkingGoose

Ok here I am posting a complete working solution with self hosted renovate on github actions. Please note that this solution is for Kotlin/Gradle/Maven based repos.

Inside .github/workflows create a renovate.yml file with the following content. Change the placeholder values according to your needs. (Keep REPLACE_WITH_GITHUB_TOKEN and REPLACE_WITH_CODEARTIFACT_AUTH_TOKEN as it is)

name: Renovate
on:
  schedule:
    - cron: '*/10 * * * *' # Set the schedule according to your preference

jobs:
  renovate:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2

      - name: Set up Node.js
        uses: actions/setup-node@v2
        with:
          node-version: 18.12.0

      - name: Install Renovate
        run: npm install -g renovate

      - name: Replace GitHub token in renovate-config.json
        run: sed -i 's/REPLACE_WITH_GITHUB_TOKEN/${{ secrets.GITHUB_TOKEN }}/g' renovate-config.json

      - name: Replace CodeArtifact token in renovate-config.json
        run: sed -i 's/REPLACE_WITH_CODEARTIFACT_AUTH_TOKEN/${{ secrets.CODEARTIFACT_AUTH_TOKEN }}/g' renovate-config.json

      - name: Run Renovate
        run: renovate
        env:
          RENOVATE_CONFIG_FILE: renovate-config.json
          CODEARTIFACT_AUTH_TOKEN: ${{ secrets.CODEARTIFACT_AUTH_TOKEN }}

Now in the root of your repo create a file named renovate-config.json with the following content. ( Keep REPLACE_WITH_CODEARTIFACT_AUTH_TOKEN here as it is too)

{
  "platform": "github",
  "endpoint": "https://api.github.com",
  "token": "<github token>",
  "enabled": true,
  "repositories": ["repoOwner/repoName"],
  "dependencyDashboard": true,
  "repositoryCache": "gradle-test-renovate-cache",
  "packageRules": [
    {
      "matchDatasources": ["maven"],
      "registryUrls": [
        "https://domain-domainOwner.d.codeartifact.region.amazonaws.com/maven/repository/"
      ]
    }
  ],
  "hostRules": [
    {
      "hostType": "maven",
      "baseUrl": "https://domain-domainOwner.d.codeartifact.region.amazonaws.com/maven/repository/",
      "token": "REPLACE_WITH_CODEARTIFACT_AUTH_TOKEN"
    }
  ]
}

This should be enough to make it work. Also if you have enabled the renovate bot on this repo or on the organizational level then in the root of repo you will see a file named " renovate.json ". Replace the content of that file with the following

{
  "enabled": true
}

Hope this will help. Thanks

faisaldbank avatar Apr 01 '23 03:04 faisaldbank

Ok here I am posting a complete working solution with self hosted renovate on github actions. Please note that this solution is for Kotlin/Gradle/Maven based repos.

Inside .github/workflows create a renovate.yml file with the following content. Change the placeholder values according to your needs. (Keep REPLACE_WITH_GITHUB_TOKEN and REPLACE_WITH_CODEARTIFACT_AUTH_TOKEN as it is)

name: Renovate
on:
  schedule:
    - cron: '*/10 * * * *' # Set the schedule according to your preference

jobs:
  renovate:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2

      - name: Set up Node.js
        uses: actions/setup-node@v2
        with:
          node-version: 18.12.0

      - name: Install Renovate
        run: npm install -g renovate

      - name: Replace GitHub token in renovate-config.json
        run: sed -i 's/REPLACE_WITH_GITHUB_TOKEN/${{ secrets.GITHUB_TOKEN }}/g' renovate-config.json

      - name: Replace CodeArtifact token in renovate-config.json
        run: sed -i 's/REPLACE_WITH_CODEARTIFACT_AUTH_TOKEN/${{ secrets.CODEARTIFACT_AUTH_TOKEN }}/g' renovate-config.json

      - name: Run Renovate
        run: renovate
        env:
          RENOVATE_CONFIG_FILE: renovate-config.json
          CODEARTIFACT_AUTH_TOKEN: ${{ secrets.CODEARTIFACT_AUTH_TOKEN }}

Now in the root of your repo create a file named renovate-config.json with the following content. ( Keep REPLACE_WITH_CODEARTIFACT_AUTH_TOKEN here as it is too)

{
  "platform": "github",
  "endpoint": "https://api.github.com",
  "token": "<github token>",
  "enabled": true,
  "repositories": ["repoOwner/repoName"],
  "dependencyDashboard": true,
  "repositoryCache": "gradle-test-renovate-cache",
  "packageRules": [
    {
      "matchDatasources": ["maven"],
      "registryUrls": [
        "https://domain-domainOwner.d.codeartifact.region.amazonaws.com/maven/repository/"
      ]
    }
  ],
  "hostRules": [
    {
      "hostType": "maven",
      "baseUrl": "https://domain-domainOwner.d.codeartifact.region.amazonaws.com/maven/repository/",
      "token": "REPLACE_WITH_CODEARTIFACT_AUTH_TOKEN"
    }
  ]
}

This should be enough to make it work. Also if you have enabled the renovate bot on this repo or on the organizational level then in the root of repo you will see a file named " renovate.json ". Replace the content of that file with the following

{
  "enabled": true
}

Hope this will help. Thanks

Is there a way how renovate can also look at the typical public registrys aswell? We disabled upstream registrys in the CodeArtifact settings. So with your settings enabled I can only update my packages that are on CodeArtifact.

Marvin0098 avatar Jul 11 '23 06:07 Marvin0098

Ok here I am posting a complete working solution with self hosted renovate on github actions. Please note that this solution is for Kotlin/Gradle/Maven based repos. Inside .github/workflows create a renovate.yml file with the following content. Change the placeholder values according to your needs. (Keep REPLACE_WITH_GITHUB_TOKEN and REPLACE_WITH_CODEARTIFACT_AUTH_TOKEN as it is)

name: Renovate
on:
  schedule:
    - cron: '*/10 * * * *' # Set the schedule according to your preference

jobs:
  renovate:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2

      - name: Set up Node.js
        uses: actions/setup-node@v2
        with:
          node-version: 18.12.0

      - name: Install Renovate
        run: npm install -g renovate

      - name: Replace GitHub token in renovate-config.json
        run: sed -i 's/REPLACE_WITH_GITHUB_TOKEN/${{ secrets.GITHUB_TOKEN }}/g' renovate-config.json

      - name: Replace CodeArtifact token in renovate-config.json
        run: sed -i 's/REPLACE_WITH_CODEARTIFACT_AUTH_TOKEN/${{ secrets.CODEARTIFACT_AUTH_TOKEN }}/g' renovate-config.json

      - name: Run Renovate
        run: renovate
        env:
          RENOVATE_CONFIG_FILE: renovate-config.json
          CODEARTIFACT_AUTH_TOKEN: ${{ secrets.CODEARTIFACT_AUTH_TOKEN }}

Now in the root of your repo create a file named renovate-config.json with the following content. ( Keep REPLACE_WITH_CODEARTIFACT_AUTH_TOKEN here as it is too)

{
  "platform": "github",
  "endpoint": "https://api.github.com",
  "token": "<github token>",
  "enabled": true,
  "repositories": ["repoOwner/repoName"],
  "dependencyDashboard": true,
  "repositoryCache": "gradle-test-renovate-cache",
  "packageRules": [
    {
      "matchDatasources": ["maven"],
      "registryUrls": [
        "https://domain-domainOwner.d.codeartifact.region.amazonaws.com/maven/repository/"
      ]
    }
  ],
  "hostRules": [
    {
      "hostType": "maven",
      "baseUrl": "https://domain-domainOwner.d.codeartifact.region.amazonaws.com/maven/repository/",
      "token": "REPLACE_WITH_CODEARTIFACT_AUTH_TOKEN"
    }
  ]
}

This should be enough to make it work. Also if you have enabled the renovate bot on this repo or on the organizational level then in the root of repo you will see a file named " renovate.json ". Replace the content of that file with the following

{
  "enabled": true
}

Hope this will help. Thanks

Is there a way how renovate can also look at the typical public registrys aswell? We disabled upstream registrys in the CodeArtifact settings. So with your settings enabled I can only update my packages that are on CodeArtifact.

Yes there is. I used that but dont remember exactly now. You can ask for chatgpt as the solution I posted was suggested by chatgpt. That was something like

registry=https://your-codeartifact-url/

@scope:registry=https://npmjs.org/

faisaldbank avatar Jul 11 '23 07:07 faisaldbank

Just checked with chatgpt and it showed below suggested way to solve your problem.

{ "platform": "github", "endpoint": "https://api.github.com", "token": "", "enabled": true, "repositories": ["repoOwner/repoName"], "dependencyDashboard": true, "repositoryCache": "gradle-test-renovate-cache", "packageRules": [ { "matchDatasources": ["maven"], "registryUrls": [ "https://domain-domainOwner.d.codeartifact.region.amazonaws.com/maven/repository/", "https://repo.maven.apache.org/maven2" ] } ], "hostRules": [ { "hostType": "maven", "baseUrl": "https://domain-domainOwner.d.codeartifact.region.amazonaws.com/maven/repository/", "token": "REPLACE_WITH_CODEARTIFACT_AUTH_TOKEN" } ] }

faisaldbank avatar Jul 11 '23 07:07 faisaldbank

WARN: Excess registryUrls found for datasource lookup - using first configured only (repository=repo123, baseBranch=develop)

28 | "datasource": "npm", 29 | "packageName": "@babel/core", 30 | "registryUrls": [ 31 | "https://domain-domainowner.d.codeartifact.eu-central-1.amazonaws.com/npm/repository", 32 | "https://registry.npmjs.org" 33 | ]

may be try this

{ "platform": "github", "endpoint": "https://api.github.com", "token": "", "enabled": true, "repositories": ["repoOwner/repoName"], "dependencyDashboard": true, "repositoryCache": "gradle-test-renovate-cache", "packageRules": [ { "matchDatasources": ["maven"], "registryUrls": [ "https://domain-domainOwner.d.codeartifact.region.amazonaws.com/maven/repository/", "https://repo.maven.apache.org/maven2" ] }, { "matchDatasources": ["npm"], "registryUrls": ["https://domain-domainowner.d.codeartifact.eu-central-1.amazonaws.com/npm/repository"] } ], "hostRules": [ { "hostType": "maven", "baseUrl": "https://domain-domainOwner.d.codeartifact.region.amazonaws.com/maven/repository/", "token": "REPLACE_WITH_CODEARTIFACT_AUTH_TOKEN" }, { "hostType": "npm", "baseUrl": "https://domain-domainowner.d.codeartifact.eu-central-1.amazonaws.com/npm/repository", "token": "REPLACE_WITH_CODEARTIFACT_AUTH_TOKEN" } ] }

faisaldbank avatar Jul 11 '23 07:07 faisaldbank

Sorry, I typed npm which made it confusing. Here just an example JUST with maven:

My code:

{
  "platform": "github",
  "endpoint": "https://api.github.com",
  "token": "",
  "enabled": true,
  "repositories": ["repoOwner/repoName"],
  "dependencyDashboard": true,
  "repositoryCache": "gradle-test-renovate-cache",
  "packageRules": [
    {
      "matchDatasources": ["maven"],
      "registryUrls": [
        "https://domain-domainOwner.d.codeartifact.region.amazonaws.com/maven/repository/",
        "https://repo.maven.apache.org/maven2"
      ]
    }
  ],
  "hostRules": [
    {
      "hostType": "maven",
      "baseUrl": "https://domain-domainOwner.d.codeartifact.region.amazonaws.com/maven/repository/",
      "token": "REPLACE_WITH_CODEARTIFACT_AUTH_TOKEN"
    }
  ]
}

The error message: WARN: Excess registryUrls found for datasource lookup - using first configured only (repository=repo123, baseBranch=develop)

I also tried adding a second hostType entry:

{
    "hostType": "maven",
    "baseUrl": "https://domain-domainOwner.d.codeartifact.region.amazonaws.com/maven/repository/",
    "token": "https://repo.maven.apache.org/maven2"
  }    

But still no luck

Marvin0098 avatar Jul 11 '23 08:07 Marvin0098

I think i found a way filtering for packageprefixes (example for npm):

      {
         "matchDatasources": ["npm"],
         "registryUrls": [
           "https://registry.npmjs.org/"
           ]
       },        
       {
         "matchDatasources": ["npm"],
         "matchPackagePrefixes": ["@myAwesomePrefix"],
         "registryUrls": [
           "https://domain-domainOwner.d.codeartifact.region.amazonaws.com/npm/repository/"
         ]
       }

Marvin0098 avatar Jul 11 '23 09:07 Marvin0098

I think i found a way filtering for packageprefixes (example for npm):

      {
         "matchDatasources": ["npm"],
         "registryUrls": [
           "https://registry.npmjs.org/"
           ]
       },        
       {
         "matchDatasources": ["npm"],
         "matchPackagePrefixes": ["@myAwesomePrefix"],
         "registryUrls": [
           "https://domain-domainOwner.d.codeartifact.region.amazonaws.com/npm/repository/"
         ]
       }

Great.. thanks

faisaldbank avatar Jul 11 '23 10:07 faisaldbank