In Google Cloud Build, what is the difference between an array of args and a long string as an arg? - google-cloud-build

While setting up a my build system to automatically version my containers in Cloud Registry (How do I set an environment or substitution variable via a step in Google Cloud Build?), I ran into a frustrating error.
This works:
- name: 'gcr.io/cloud-builders/docker'
entrypoint: 'bash'
args: ['-c', 'docker build -t gcr.io/$PROJECT_ID/$REPO_NAME:$SHORT_SHA -t gcr.io/$PROJECT_ID/$REPO_NAME:latest -t gcr.io/$PROJECT_ID/$REPO_NAME:$(cat VERSION) -t gcr.io/$PROJECT_ID/$REPO_NAME:$(cat SEMVER) -t gcr.io/$PROJECT_ID/$REPO_NAME:$(cat MAJOR) -t gcr.io/$PROJECT_ID/$REPO_NAME:$(cat MAJOR).$(cat MINOR) .']
images: ['gcr.io/$PROJECT_ID/$REPO_NAME']
But this does not work:
- name: 'gcr.io/cloud-builders/docker'
entrypoint: 'bash'
args: ['-c', 'docker', 'build',
'-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:$SHORT_SHA',
'-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:latest',
'-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:$(cat VERSION)',
'-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:$(cat SEMVER)',
'-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:$(cat MAJOR)',
'-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:$(cat MAJOR).$(cat MINOR)',
'.']
images: ['gcr.io/$PROJECT_ID/$REPO_NAME']
Shouldn't these be equivalent? What am I missing?

Try this one.
```
- name: 'gcr.io/cloud-builders/docker'
entrypoint: 'bash'
args:
- -c
- |
docker build -t gcr.io/$PROJECT_ID/$REPO_NAME:$SHORT_SHA \
-t gcr.io/$PROJECT_ID/$REPO_NAME:latest \
-t gcr.io/$PROJECT_ID/$REPO_NAME:$(cat VERSION) \
-t gcr.io/$PROJECT_ID/$REPO_NAME:$(cat SEMVER) \
-t gcr.io/$PROJECT_ID/$REPO_NAME:$(cat MAJOR) \
-t gcr.io/$PROJECT_ID/$REPO_NAME:$(cat MAJOR).$(cat MINOR) .
images: ['gcr.io/$PROJECT_ID/$REPO_NAME']
```
and one way this could work is. Just removing the entry point from bash.
```
- name: 'gcr.io/cloud-builders/docker'
args: [ 'build',
'-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:$SHORT_SHA',
'-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:latest',
'-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:$(cat VERSION)',
'-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:$(cat SEMVER)',
'-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:$(cat MAJOR)',
'-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:$(cat MAJOR).$(cat MINOR)',
'.']
images: ['gcr.io/$PROJECT_ID/$REPO_NAME']
```

Related

GitHub Workflow matrix outputs return empty values

I have the following workflow for GitHub actions
it runs a matrix to build and publish images and at the end saves the image name to the outputs environment
and then uses those outputs as a parameter to the run command
name: Build and Deploy
on:
push:
branches: [main]
# Publish semver tags as releases.
tags: ["v*.*.*"]
env:
jobs:
set-env:
name: Set Environment Variables
runs-on: ubuntu-latest
outputs:
version: ${{ steps.main.outputs.version }}
created: ${{ steps.main.outputs.created }}
# repository: ${{ steps.main.outputs.repository }}
steps:
- id: main
run: |
echo "version=$(echo ${GITHUB_SHA} | cut -c1-7)" >> $GITHUB_OUTPUT
echo "created=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
package-services:
runs-on: ubuntu-latest
needs: set-env
permissions:
contents: read
packages: write
outputs:
containerImage-disposal: ${{ steps.image-tag.outputs.image-disposal-web-api }}
containerImage-executive: ${{ steps.image-tag.outputs.image-executive-web-api }}
containerImage-finance-management: ${{ steps.image-tag.outputs.image-finance-management-web-api }}
containerImage-fm-background-task: ${{ steps.image-tag.outputs.image-finance-management-background-tasks-web-api }}
containerImage-legal: ${{ steps.image-tag.outputs.image-legal-web-api }}
containerImage-licensing: ${{ steps.image-tag.outputs.image-licensing-web-api }}
containerImage-vehicle-management: ${{ steps.image-tag.outputs.vehicle-management-web-api }}
containerImage-vehicle-rental: ${{ steps.image-tag.outputs.vehicle-rental-web-api }}
containerImage-workshop-management: ${{ steps.image-tag.outputs.workshop-management-web-api }}
containerImage-wm-clearance: ${{ steps.image-tag.outputs.workshop-management-clearance-web-api }}
strategy:
matrix:
services:
[
{
"appName": "disposal-web-api"
},
{
"appName": "executive-web-api"
},
{
"appName": "finance-management-web-api"
},
{
"appName": "finance-management-background-tasks"
},
{
"appName": "legal-web-api"
},
{
"appName": "licensing-web-api"
},
{
"appName": "vehicle-management-web-api"
},
{
"appName": "vehicle-rental-web-api"
},
{
"appName": "workshop-management-web-api"
},
{
"appName": "workshop-management-clearance-web-api"
},
]
steps:
- name: Checkout repository
uses: actions/checkout#v3
- name: Output image tag
id: image-tag
run: |
echo "image-${{ matrix.services.appName }}=${{env.IMAGE_NAME }}.${{ matrix.services.appName }}:sha-${{ needs.set-env.outputs.version }}" >> $GITHUB_OUTPUT
deploy:
if: ${{ github.event_name != 'pull_request' }}
runs-on: ubuntu-latest
needs: [package-services]
steps:
- name: Checkout repository
uses: actions/checkout#v2
- name: Azure Login
uses: azure/login#v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Deploy bicep
uses: azure/CLI#v1
with:
inlineScript: |
az group create -g ifms-gmt-demo-westeurope -l westeurope
az deployment group create -g ifms-gmt-demo-westeurope \
-f ./deploy/containerapps/main.bicep \
-p \
imageNamespace=${{ secrets.DOCKERHUB_USERNAME }} \
postgresAdministratorLogin=${{ secrets.POSTGRES_ADMINISTRATOR_LOGIN_DEMO }} \
postgresAdministratorLoginPassword=${{ secrets.POSTGRES_ADMINISTRATOR_LOGIN_PASSWORD_DEMO }} \
disposalImage='${{ needs.package-services.outputs.containerImage-disposal }}' \
executiveImage='${{ needs.package-services.outputs.containerImage-executive }}' \
financeManagementImage='${{ needs.package-services.outputs.containerImage-finance-management }}' \
financeManagementBackgroundTasksImage='${{ needs.package-services.outputs.containerImage-fm-background-tasks }}' \
legalImage='${{ needs.package-services.outputs.containerImage-legal }}' \
licensingImage='${{ needs.package-services.outputs.containerImage-licensing }}' \
vehicleManagementImage='${{ needs.package-services.outputs.containerImage-vehicle-management }}' \
vehicleRentalImage='${{ needs.package-services.outputs.containerImage-vehicle-rental }}' \
workshopManagementImage='${{ needs.package-services.outputs.containerImage-workshop-management }}' \
workshopManagementClearanceImage='${{ needs.package-services.outputs.containerImage-wm-clearance }}'
for some weird reason all parameters get populated except for vehicleManagementImage vehicleRentalImage workshopManagementImage workshopManagementClearanceImage
as can be seen, below they have an empty string
Run azure/CLI#v1
with:
inlineScript: az group create -g ifms-gmt-demo-westeurope -l westeurope
az deployment group create -g ifms-gmt-demo-westeurope \
-f ./deploy/containerapps/main.bicep \
-p \
imageNamespace=*** \
postgresAdministratorLogin=*** \
postgresAdministratorLoginPassword=*** \
disposalImage='ifms.gmt.disposal-web-api:sha-4a1bf13' \
executiveImage='ifms.gmt.executive-web-api:sha-4a1bf13' \
financeManagementImage='ifms.gmt.finance-management-web-api:sha-4a1bf13' \
financeManagementBackgroundTasksImage='' \
legalImage='ifms.gmt.legal-web-api:sha-4a1bf13' \
licensingImage='ifms.gmt.licensing-web-api:sha-4a1bf13' \
vehicleManagementImage='' \
vehicleRentalImage='' \
workshopManagementImage='' \
workshopManagementClearanceImage=''

sh Job container after it is stopped

I am backuping my Postgresql database using this cronjob:
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: postgres-backup
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: postgres-backup
image: postgres:10.4
command: ["/bin/sh"]
args: ["-c", 'echo "$PGPASS" > /root/.pgpass && chmod 600 /root/.pgpass && pg_dump -Fc -h <host> -U <user> <db> > /var/backups/backup.dump']
env:
- name: PGPASS
valueFrom:
secretKeyRef:
name: pgpass
key: pgpass
volumeMounts:
- mountPath: /var/backups
name: postgres-backup-storage
restartPolicy: Never
volumes:
- name: postgres-backup-storage
hostPath:
path: /var/volumes/postgres-backups
type: DirectoryOrCreate
The cronjob gets successfully executed, the backup is made and saved in the container of the Job but this container is stopped after successful execution of the script.
of course I want to access the backup files in the container but I can't because it is stopped/terminated.
is there a way to execute shell commands in a container after it is terminated, so I can access the backup files saved in the container?
I know that I could do that on the node, but I don't have the permission to access it.
#confused genius gave me a great idea to create another same container to access the dump files so this is the solution that works:
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: postgres-backup
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: postgres-backup
image: postgres:10.4
command: ["/bin/sh"]
args: ["-c", 'echo "$PGPASS" > /root/.pgpass && chmod 600 /root/.pgpass && pg_dump -Fc -h <host> -U <user> <db> > /var/backups/backup.dump']
env:
- name: PGPASS
valueFrom:
secretKeyRef:
name: dev-pgpass
key: pgpass
volumeMounts:
- mountPath: /var/backups
name: postgres-backup-storage
- name: postgres-restore
image: postgres:10.4
volumeMounts:
- mountPath: /var/backups
name: postgres-backup-storage
restartPolicy: Never
volumes:
- name: postgres-backup-storage
hostPath:
# Ensure the file directory is created.
path: /var/volumes/postgres-backups
type: DirectoryOrCreate
after that one just needs to sh into the "postgres-restore" container and access the dump files.
thanks

Changing directories in Cloud Build 'cd' no found

I'm using cloud build to clone a repository. I can confirm the repository clones successfully to the cloud build /workspace volume.
steps:
- id: 'Clone repository'
name: 'gcr.io/cloud-builders/git'
args: ['clone', $_REPO_URL]
volumes:
- name: 'ssh'
path: /root/.ssh
I then run the next step to confirm
- id: 'List'
name: 'alpine'
args: ['ls']
and it shows me the repository is in the current directory. But when I try and cd into the directory the cd command doesn't work and throws an error:
ERROR: build step 3 "alpine" failed: starting step container failed: Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "cd <repo-name>": executable file not found in $PATH: unknown
My ultimate goal is to cd into the repository and run some git commands. I use alpine later on because the git builder image doesn't allow me to use cd either.
substitutions:
_REPO_NAME: 'test-repo'
_REPO_URL: 'git#bitbucket.org:example/test-repo.git'
_BRANCH_NAME: 'feature/something'
steps:
- id: 'Clone repository'
name: 'gcr.io/cloud-builders/git'
args: ['clone', $_REPO_URL]
volumes:
- name: 'ssh'
path: /root/.ssh
- id: 'Check Diff'
name: 'alpine'
args: ['cd $_REPO_NAME', '&&', 'git checkout $_BRANCH_NAME', '&&', 'git diff main --name-only']
You can use bash to run any commands you would like.
Here is one example I use for one of my projects:
- name: 'gcr.io/cloud-builders/git'
id: Clone env repository
entrypoint: /bin/sh
args:
- '-c'
- |
git clone git#github.com:xyz/abc.git && \
cd gitops-env-repo/ && \
git checkout dev
Use dir field in your *.yaml file.
steps:
- name: string
args: [string, string, ...]
env: [string, string, ...]
dir: string
id: string
waitFor: [string, string, ...]
entrypoint: string
secretEnv: string
volumes: object(Volume)
timeout: string (Duration format)
- name: string
...
- name: string
...
timeout: string (Duration format)
queueTtl: string (Duration format)
logsBucket: string
options:
env: [string, string, ...]
secretEnv: string
volumes: object(Volume)
sourceProvenanceHash: enum(HashType)
machineType: enum(MachineType)
diskSizeGb: string (int64 format)
substitutionOption: enum(SubstitutionOption)
dynamicSubstitutions: boolean
logStreamingOption: enum(LogStreamingOption)
logging: enum(LoggingMode)
pool: object(PoolOption)
substitutions: map (key: string, value: string)
tags: [string, string, ...]
serviceAccount: string
secrets: object(Secret)
availableSecrets: object(Secrets)
artifacts: object (Artifacts)
images:
- [string, string, ...]
https://cloud.google.com/build/docs/build-config-file-schema

Equivalent of healthcheck test in GitLab CI/CD

I have this docker-compose.yml
version: '3.4'
services:
localstack:
image: localstack/localstack:0.11.2
environment:
- AWS_DEFAULT_REGION=us-east-1
- EDGE_PORT=4566
- SERVICES=sns,sqs
ports:
- "4566:4566"
volumes:
- "${TMPDIR:-/tmp/localstack}:/tmp/localstack"
- "/var/run/docker.sock:/var/run/docker.sock"
healthcheck:
test:
- CMD
- bash
- -c
- awslocal sns list-topics
&& awslocal sqs list-queues
interval: 5s
timeout: 10s
retries: 10
with that, it checks the healtheck taking advantage that the container has awslocal command available.
What would be the equivalent for GitLab assuming I need to run some tests against localstack and I want to wait for localstack service to be up and running before?
Something like this:
tests:
stage: test
services:
- name: localstack/localstack:0.11.2
alias: localstack
variables:
AWS_DEFAULT_REGION: "us-east-1"
EDGE_PORT: "4566"
SERVICES: "sns,sqs"
before_script:
# should I use before_script? is there a better way? What's the linux command to test the same and wait?
script: dotnet test --blame --configuration Release
rules:
- exists:
- test/**/*Tests.csproj
UPDATE 1:
One possible trick is using the before_script, but happy to hear about better built-in solutions. Also this does not work as
/bin/bash: line 125: awslocal: command not found
Failed rounds=9
but here's my attempt
tests:
stage: test
services:
- name: localstack/localstack:0.11.2
alias: localstack
variables:
AWS_DEFAULT_REGION: "us-east-1"
EDGE_PORT: "4566"
SERVICES: "sns,sqs"
before_script:
- rounds=10;
while [ $rounds -gt 0 ]; do
awslocal sns list-topics && awslocal sqs list-queues && echo OK && break || echo Failed
rounds=$(($rounds - 1));
sleep 5;
done;
script: dotnet test --blame --configuration Release
rules:
- exists:
- test/**/*Tests.csproj
Here's a possible working solution. Notice that I cannot use awslocal within the service despite of having awslocal available inside the localstack container (maybe something needs to be mounted).
In any case I solved it with a simple curl.
tests:
stage: test
services:
- name: localstack/localstack:0.11.2
alias: localstack
variables:
AWS_DEFAULT_REGION: "us-east-1"
EDGE_PORT: "4566"
SERVICES: "sns,sqs"
before_script:
- rounds=10;
while [ $rounds -gt 0 ]; do
curl http://localstack:4566 && echo OK && break || echo FAIL
rounds=$rounds - 1;
sleep 5;
done;
script: dotnet test --blame --configuration Release
rules:
- exists:
- test/**/*Tests.csproj

Pass variable build args to docker build command

Here is the docker stage:
- template: ../docker-template.yml
parameters:
buildArgs:
- name: Arg1
value: $(arg1-value)
- name: Arg2
value: $(arg2-value)
docker-template.yml
parameters:
buildArgs: []
stages:
- stage: Docker
displayName: Docker Stage
jobs:
- job: Docker
steps:
# - ${{ each arg in parameters.buildArgs }}:
# - bash: |
# buildArgString+=' --build-arg ${{arg.name}}=${{arg.value}}'
# displayName: "Getting Params"
- bash: |
${{ each arg in parameters.buildArgs }}:
buildArgString+=' --build-arg ${{arg.name}}=${{arg.value}}'
echo 'buildstring=$buildArgString'
displayName: 'build string'
- bash: |
cd $(sourceDirectory)
docker build \
-t $(registryName)/$(imageName):$(imageTag) \
$buildArgString \
.
failOnStderr: true
displayName: 'docker build'
Here I want to construct buildArgString based on the parameters passed and pass it to docker build command as shown. When I use this I get error
The directive 'each' is not allowed in this context. Directives are not supported for expressions that are embedded within a string. Directives are only supported when the entire value is an expression.
Any suggestions?
As the message mentioned, you are using each in a wrong place. Try with below script sample:
azure-pipelines.yml:
extends:
template: docker-template.yml
parameters:
buildArgs:
Arg1 : $(arg1-value)
Arg2 : $(arg2-value)
docker-template.yml:
parameters:
- name: buildArgs
type: object
default: []
stages:
- stage: Docker
displayName: Docker Stage
jobs:
- job: Docker
steps:
- ${{ each arg in parameters.buildArgs }}:
- bash: |
echo ${{ arg.key }}
echo ${{ arg.value }}
echo "##vso[task.setvariable variable=buildArgStringOther]--build-arg ${{ arg.key }}=${{ arg.value }}"
displayName: ${{ arg.key }}
- bash: |
echo "buildstring=$(buildArgStringOther)"
displayName: ECHO-${{ arg.key }}

Resources