Compare commits

..

37 Commits

Author SHA1 Message Date
Ompragash Viswanathan 3674493911 update kaniko-executor base image to 1.25.0 from chaingaurds maintained fork 2025-07-02 09:30:10 +05:30
OP (oppenheimer) a879280371 push-only support to Kaniko ACR (#148) 2025-06-03 22:17:47 +05:30
OP (oppenheimer) 809fadc203 feat: [CI-17517]: Add push-only support for Kaniko-GAR (#147)
* Add push-only support to Kaniko-GAR

* Refactor GAR authentication and crane push to use Application Default Credentials

* Add robust GAR authentication with Docker config and crane options

* GAR authentication setup and remove redundant logging statements
2025-05-13 21:30:09 +05:30
ompragash.viswanathan@harness.io 87ca9fe1b7 Update pipeline drone-kaniko-harness 2025-05-12 20:38:33 +05:30
OP (oppenheimer) a091f2ad04 ECR auth for push-only operation + code refactoring (#144) 2025-04-25 18:11:30 +05:30
OP (oppenheimer) af2add0aa5 Update pipeline drone-kaniko-harness (#143) 2025-04-09 19:24:48 +05:30
OP (oppenheimer) 58bd727c07 feat: [CI-16588]: Add support to PLUGIN_TAR_PATH, PLUGIN_SOURCE_TAR_PATH and PLUGIN_PUSH_ONLY to kaniko-ecr (#141)
* Add support for tar-path, source-tar-path and push-only operations

* Updated cmd/kaniko-ecr/main.go

* Updated cmd/kaniko-ecr/main.go

* Update cmd/kaniko-ecr/main.go
2025-03-24 21:31:18 +05:30
ci-reporunner a73b8ee28d Update pipeline drone-kaniko-harness (#142)
Co-authored-by: ompragash.viswanathan@harness.io <ompragash.viswanathan@harness.io>
2025-03-20 19:10:13 +05:30
OP (oppenheimer) b826c7f408 feat: [CI-16392]: Authenticate And Pull Private Base Images when NO_PUSH is enabled (#140) 2025-03-06 20:32:35 +05:30
ci-reporunner e56198f84c Create pipeline drone-kaniko-harness (#136) 2025-03-04 19:18:44 +05:30
Devansh Mathur d6153866df feat: [CI-16330]: Adding default OutputFile as DRONE_OUTPUT. (#139)
* Adding default OutputFile as DRONE_OUTPUT.

* Removing if checks and optimizing setting up of default OutputFile as DRONE_OUTPUT.
2025-03-03 18:05:21 +05:30
OP (oppenheimer) 30e1ea9fd8 Update main.go (#138) 2025-02-07 14:28:32 +05:30
OP (oppenheimer) 0fb726616e feat: [CI-16193]: Support multiple ignore paths (#137)
* add new input ignore_paths to accept multiple values

* Support new input ignore_paths to all the supported versions of Kaniko
2025-02-07 11:46:23 +05:30
OP (oppenheimer) 334f6191d1 Add Push-only support to Kaniko (Docker) (#135) 2024-12-20 11:17:28 +05:30
sahithibanda01 a3af953651 fix: [CI-14845]: support for build args which has comma seperated values (#133) 2024-11-29 13:12:39 +05:30
Anshika Anand e6ab8aa3c0 feat:[CI-15236]: Added IMAGE_TAR_PATH as output variable for the plugin. (#132)
* feat:[CI-15236]: Added PLUGIN_TAR_PATH as output variable for the plugin.

* feat:[CI-15236]: Added PLUGIN_TAR_PATH as output variable for the plugin.

* feat:[CI-15236]: Added PLUGIN_TAR_PATH as output variable for the plugin.

* feat:[CI-15236]: Added PLUGIN_TAR_PATH as output variable for the plugin.

* feat:[CI-15236]: Test commit.

* feat:[CI-15236]: Test commit.

* feat:[CI-15236]: Added PLUGIN_TAR_PATH as output variable for the plugin.

* feat:[CI-15236]: Directory check and UTs.

* Update pkg/output/output.go

* feat:[CI-15236]: fixes

* feat:[CI-15236]: Test fixes - removed root

* feat:[CI-15236]: Test fixes - removed root

* feat:[CI-15236]: Test fixes - removed root

* feat:[CI-15236]: If tarPath directory no present it will create it.

* feat:[CI-15236]: If tarPath directory no present it will create it.

* feat:[CI-15236]: fixes

* Update kaniko.go

removed as getTarPath is only called when tarPath isn't empty

---------

Co-authored-by: OP (oppenheimer) <21008429+Ompragash@users.noreply.github.com>
2024-11-28 18:31:10 +05:30
Abhay 113a61b0e1 feat: [Ci-14242]: add oidc support for ecr (#131)
* feat: [Ci-14242]: add oidc support for ecr

* fix: [CI-14242]: error handling for oidc kakniko-ecr
2024-10-17 01:51:09 +05:30
rahkumar56 982c141391 feat: [CI-13961]: Upgraded gcr.io/kaniko-project/executor version to v1.23.2. (#125) 2024-09-09 11:23:40 +05:30
Aishwarya Lad f41d7cb836 upgrade kaniko version for chmod on ADD and COPY (#121) 2024-07-10 19:44:09 +05:30
rahkumar56 a71177d4b4 fix: [CI-13178]: Upgrade go version with minor version to 1.22.4 (#120) 2024-07-10 16:06:42 +05:30
rahkumar56 5582e3ed7c feat: [CI-13178]: GO version upgrade from 1.22.0 to 1.22.4. (#119) 2024-07-03 12:30:41 +05:30
Hemanth Mantri 4c0f781999 [CI-13182]: Use snapshot-mode instead of snapshotMode (#118)
The kaniko flag `snapshotMode` is deprecated in favor of `snapshot-mode`. The default value of `snapshot-mode` is `full`. As a result, the `optimize` flag set in Harness CI's `BuildAndPushToDocker` steps doesn't behave as expected because the `full` mode triggers a full filesystem scan which is slow. The `optimize` flag in Harness translates to `snapshot-mode=redo` which means only filesystem deltas are compared which is much faster. 

This change fixes the flag name being sent to Kaniko executor. I verified that the executor present in the current version already supports this flag as shown below:

```
hemanthkumarmantri@Hemanth Kumar harness-core % docker run -it --entrypoint /kaniko/executor plugins/kaniko:1.8.10 --help | grep snapshot-mode
      --snapshot-mode string                      Change the file attributes inspected during snapshotting (default "full")
      --snapshotMode string                       This flag is deprecated. Please use '--snapshot-mode'.
```
2024-07-01 14:06:19 -07:00
Abhay 20525e5403 feat: [CI-10849]: add git-leaks support (#113)
* feat: [CI-10849]: add git-leaks support

* correct reference
2024-05-10 12:38:31 +01:00
Aishwarya Lad 04885c8ec8 fix tests (#117) 2024-05-01 15:14:04 -04:00
Aishwarya Lad e4a0486a6e modify base image registry variable (#116)
* modify base image registry variable

* adding instead of modifying

* adding instead of modifying
2024-04-29 14:26:31 -04:00
Aishwarya Lad 7b442a53ff Modify docker config to add base connector (#115)
* add config for base connector

* fix permissions code

* add gar step support

* add gar step support

* reformat code, add support for gar and acr

* remove logs

* address review comments

* delete bin file
2024-04-26 15:54:08 -04:00
Abhay f224543240 fix: [CI-11358]: fix kaniko executor Vulnerabilities (#114) 2024-03-20 12:57:59 +05:30
rahkumar56 910bcb89c2 Update GO Version to latest version in all the files (#112) 2024-03-05 23:15:25 +05:30
Soumyajit Das 44ccf5a7c6 fix: [CI-10165]: Add additional kaniko args (#102)
* fix: [CI-10165]: Add additional kaniko args

* fix: [CI-10165]: support additional kaniko args

* fix: [CI-10165]: Add bool parse logic
2024-02-06 15:22:31 +05:30
Rojan Dinc f8c678fcde Revert "fix: [CI-10165]: support additional kaniko args from PLUGIN_ env vars (#93)" (#99)
This reverts commit 20c593c3e7.
2024-01-30 21:33:04 +05:30
Soumyajit Das 20c593c3e7 fix: [CI-10165]: support additional kaniko args from PLUGIN_ env vars (#93) 2024-01-30 12:31:18 +05:30
Vistaar Juneja c2f00d6d86 follow next link for azure subscriptions API (#94) 2024-01-25 09:35:14 +00:00
Soumyajit Das 467287429a fix: [CI-10908]: added kaniko 1.19.2 suport for dockerignore (#92) 2024-01-12 16:17:51 +05:30
Vistaar Juneja 65cd3884f1 add validations on response from /subscriptions API in ACR (#91) 2024-01-10 10:48:31 +00:00
Eoin McAfee 5df1d55e7f Split out gcr and gar (#88)
* seperate out gcr and gar
2023-11-23 09:26:55 +00:00
Abhay 3181dc066f [fix]: [ci-9254]: go version upgrade to 1.21 (#86) 2023-09-13 14:55:14 +05:30
Aman Singh a26a84a1fe Revert "fix: [CI-8113]: added kaniko 1.9.2 support (#83)" (#84)
This reverts commit cd3745b3ca.
2023-06-01 10:28:40 +05:30
58 changed files with 4458 additions and 505 deletions
+123 -20
View File
@@ -1,10 +1,17 @@
kind: pipeline
type: docker
type: vm
name: default
pool:
use: ubuntu
platform:
os: linux
arch: amd64
steps:
- name: build
image: golang:1.19
image: golang:1.22.4
commands:
- go test ./...
- sh scripts/build.sh
@@ -43,6 +50,23 @@ steps:
exclude:
- pull_request
- name: gar
image: plugins/docker
settings:
repo: plugins/kaniko-gar
auto_tag: true
auto_tag_suffix: linux-amd64
daemon_off: false
dockerfile: docker/gar/Dockerfile.linux.amd64
username:
from_secret: docker_username
password:
from_secret: docker_password
when:
event:
exclude:
- pull_request
- name: ecr
image: plugins/docker
settings:
@@ -82,9 +106,9 @@ steps:
settings:
repo: plugins/kaniko
auto_tag: true
auto_tag_suffix: linux-amd64-kaniko1.9.2
auto_tag_suffix: linux-amd64-kaniko1.9.1
daemon_off: false
dockerfile: docker/docker/Dockerfile.linux.amd64.kaniko1.9.2
dockerfile: docker/docker/Dockerfile.linux.amd64.kaniko1.9.1
username:
from_secret: docker_username
password:
@@ -99,9 +123,9 @@ steps:
settings:
repo: plugins/kaniko-gcr
auto_tag: true
auto_tag_suffix: linux-amd64-kaniko1.9.2
auto_tag_suffix: linux-amd64-kaniko1.9.1
daemon_off: false
dockerfile: docker/gcr/Dockerfile.linux.amd64.kaniko1.9.2
dockerfile: docker/gcr/Dockerfile.linux.amd64.kaniko1.9.1
username:
from_secret: docker_username
password:
@@ -111,15 +135,31 @@ steps:
exclude:
- pull_request
- name: gar-kaniko-v1-9
image: plugins/docker
settings:
repo: plugins/kaniko-gar
auto_tag: true
auto_tag_suffix: linux-amd64-kaniko1.9.1
daemon_off: false
dockerfile: docker/gar/Dockerfile.linux.amd64.kaniko1.9.1
username:
from_secret: docker_username
password:
from_secret: docker_password
when:
event:
exclude:
- pull_request
- name: ecr-kaniko-v1-9
image: plugins/docker
settings:
repo: plugins/kaniko-ecr
auto_tag: true
auto_tag_suffix: linux-amd64-kaniko1.9.2
auto_tag_suffix: linux-amd64-kaniko1.9.1
daemon_off: false
dockerfile: docker/ecr/Dockerfile.linux.amd64.kaniko1.9.2
dockerfile: docker/ecr/Dockerfile.linux.amd64.kaniko1.9.1
username:
from_secret: docker_username
password:
@@ -138,7 +178,7 @@ pool:
steps:
- name: build
image: golang:1.19
image: golang:1.22.4
commands:
- go test ./...
- sh scripts/build.sh
@@ -177,6 +217,23 @@ steps:
exclude:
- pull_request
- name: gar
image: plugins/docker
settings:
repo: plugins/kaniko-gar
auto_tag: true
auto_tag_suffix: linux-arm64
daemon_off: false
dockerfile: docker/gar/Dockerfile.linux.arm64
username:
from_secret: docker_username
password:
from_secret: docker_password
when:
event:
exclude:
- pull_request
- name: ecr
image: plugins/docker
settings:
@@ -216,9 +273,9 @@ steps:
settings:
repo: plugins/kaniko
auto_tag: true
auto_tag_suffix: linux-arm64-kaniko1.9.2
auto_tag_suffix: linux-arm64-kaniko1.9.1
daemon_off: false
dockerfile: docker/docker/Dockerfile.linux.arm64.kaniko1.9.2
dockerfile: docker/docker/Dockerfile.linux.arm64.kaniko1.9.1
username:
from_secret: docker_username
password:
@@ -233,9 +290,9 @@ steps:
settings:
repo: plugins/kaniko-gcr
auto_tag: true
auto_tag_suffix: linux-arm64-kaniko1.9.2
auto_tag_suffix: linux-arm64-kaniko1.9.1
daemon_off: false
dockerfile: docker/gcr/Dockerfile.linux.arm64.kaniko1.9.2
dockerfile: docker/gcr/Dockerfile.linux.arm64.kaniko1.9.1
username:
from_secret: docker_username
password:
@@ -245,15 +302,31 @@ steps:
exclude:
- pull_request
- name: gar-kaniko-v1-9
image: plugins/docker
settings:
repo: plugins/kaniko-gar
auto_tag: true
auto_tag_suffix: linux-arm64-kaniko1.9.1
daemon_off: false
dockerfile: docker/gar/Dockerfile.linux.arm64.kaniko1.9.1
username:
from_secret: docker_username
password:
from_secret: docker_password
when:
event:
exclude:
- pull_request
- name: ecr-kaniko-v1-9
image: plugins/docker
settings:
repo: plugins/kaniko-ecr
auto_tag: true
auto_tag_suffix: linux-arm64-kaniko1.9.2
auto_tag_suffix: linux-arm64-kaniko1.9.1
daemon_off: false
dockerfile: docker/ecr/Dockerfile.linux.arm64.kaniko1.9.2
dockerfile: docker/ecr/Dockerfile.linux.arm64.kaniko1.9.1
username:
from_secret: docker_username
password:
@@ -264,9 +337,12 @@ steps:
- pull_request
---
kind: pipeline
type: docker
type: vm
name: notifications-docker
pool:
use: ubuntu
platform:
os: linux
arch: amd64
@@ -296,6 +372,18 @@ steps:
username:
from_secret: docker_username
- name: manifest-gar
pull: always
image: plugins/manifest
settings:
auto_tag: true
ignore_missing: true
password:
from_secret: docker_password
spec: docker/gar/manifest.tmpl
username:
from_secret: docker_username
- name: manifest-acr
pull: always
image: plugins/manifest
@@ -331,9 +419,12 @@ depends_on:
---
kind: pipeline
type: docker
type: vm
name: notifications-docker-kaniko1-8
pool:
use: ubuntu
platform:
os: linux
arch: amd64
@@ -347,7 +438,7 @@ steps:
ignore_missing: true
password:
from_secret: docker_password
spec: docker/docker/manifest-kaniko1.9.2.tmpl
spec: docker/docker/manifest-kaniko1.9.1.tmpl
username:
from_secret: docker_username
@@ -359,7 +450,19 @@ steps:
ignore_missing: true
password:
from_secret: docker_password
spec: docker/gcr/manifest-kaniko1.9.2.tmpl
spec: docker/gcr/manifest-kaniko1.9.1.tmpl
username:
from_secret: docker_username
- name: manifest-gar
pull: always
image: plugins/manifest
settings:
auto_tag: false
ignore_missing: true
password:
from_secret: docker_password
spec: docker/gar/manifest-kaniko1.9.1.tmpl
username:
from_secret: docker_username
@@ -371,7 +474,7 @@ steps:
ignore_missing: true
password:
from_secret: docker_password
spec: docker/ecr/manifest-kaniko1.9.2.tmpl
spec: docker/ecr/manifest-kaniko1.9.1.tmpl
username:
from_secret: docker_username
+14
View File
@@ -0,0 +1,14 @@
inputSet:
name: event-PR
identifier: eventPR
orgIdentifier: default
projectIdentifier: Drone_Plugins
pipeline:
identifier: dronekanikoharness
properties:
ci:
codebase:
build:
type: PR
spec:
number: <+trigger.prNumber>
+14
View File
@@ -0,0 +1,14 @@
inputSet:
name: event-Push
identifier: eventPush
orgIdentifier: default
projectIdentifier: Drone_Plugins
pipeline:
identifier: dronekanikoharness
properties:
ci:
codebase:
build:
type: branch
spec:
branch: <+trigger.branch>
+14
View File
@@ -0,0 +1,14 @@
inputSet:
name: event-Tag
identifier: eventTag
orgIdentifier: default
projectIdentifier: Drone_Plugins
pipeline:
identifier: dronekanikoharness
properties:
ci:
codebase:
build:
type: tag
spec:
tag: <+trigger.tag>
+656
View File
@@ -0,0 +1,656 @@
pipeline:
name: drone-kaniko-harness
identifier: dronekanikoharness
projectIdentifier: Drone_Plugins
orgIdentifier: default
tags: {}
properties:
ci:
codebase:
connectorRef: GitHub_Drone_Org
repoName: drone-kaniko
build: <+input>
sparseCheckout: []
stages:
- parallel:
- stage:
name: linux-amd64
identifier: linuxamd64
description: ""
type: CI
spec:
cloneCodebase: true
caching:
enabled: false
paths: []
platform:
os: Linux
arch: Amd64
runtime:
type: Cloud
spec: {}
execution:
steps:
- step:
type: Run
name: Build Binary
identifier: Build
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: golang:1.23.0
shell: Sh
command: |-
go test ./...
sh scripts/build.sh
- parallel:
- step:
type: Plugin
name: BuildAndPushDockerTag
identifier: BuildAndPushDockerTag
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: plugins/docker
settings:
username: drone
password: <+secrets.getValue("Plugins_Docker_Hub_Pat")>
repo: plugins/kaniko<+matrix.image>
dockerfile: docker/<+matrix.repo>/Dockerfile.linux.amd64
auto_tag: "true"
auto_tag_suffix: linux-amd64
daemon_off: "false"
when:
stageStatus: Success
condition: <+codebase.build.type> == "tag"
strategy:
matrix:
image:
- ""
- "-gcr"
- "-gar"
- "-ecr"
- "-acr"
repo:
- docker
- gcr
- gar
- ecr
- acr
exclude:
- image: ""
repo: gcr
- image: ""
repo: gar
- image: ""
repo: ecr
- image: ""
repo: acr
- image: "-gcr"
repo: docker
- image: "-gcr"
repo: gar
- image: "-gcr"
repo: ecr
- image: "-gcr"
repo: acr
- image: "-gar"
repo: docker
- image: "-gar"
repo: gcr
- image: "-gar"
repo: ecr
- image: "-gar"
repo: acr
- image: "-ecr"
repo: docker
- image: "-ecr"
repo: gcr
- image: "-ecr"
repo: gar
- image: "-ecr"
repo: acr
- image: "-acr"
repo: docker
- image: "-acr"
repo: gcr
- image: "-acr"
repo: gar
- image: "-acr"
repo: ecr
nodeName: _<+matrix.repo>
- step:
type: Plugin
name: BuildAndPushDockerTag_Kaniko
identifier: BuildAndPushDockerTag_Kaniko
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: plugins/docker
settings:
username: drone
password: <+secrets.getValue("Plugins_Docker_Hub_Pat")>
repo: plugins/kaniko<+matrix.image>
dockerfile: docker/<+matrix.repo>/Dockerfile.linux.amd64.kaniko1.9.1
auto_tag: "true"
auto_tag_suffix: linux-amd64-kaniko1.9.1
daemon_off: "false"
when:
stageStatus: Success
condition: <+codebase.build.type> == "tag"
strategy:
matrix:
image:
- ""
- "-gcr"
- "-gar"
- "-ecr"
repo:
- docker
- gcr
- gar
- ecr
exclude:
- image: ""
repo: gcr
- image: ""
repo: gar
- image: ""
repo: ecr
- image: "-gcr"
repo: docker
- image: "-gcr"
repo: gar
- image: "-gcr"
repo: ecr
- image: "-gar"
repo: docker
- image: "-gar"
repo: gcr
- image: "-gar"
repo: ecr
- image: "-ecr"
repo: docker
- image: "-ecr"
repo: gcr
- image: "-ecr"
repo: gar
nodeName: <+matrix.repo>
- parallel:
- step:
type: BuildAndPushDockerRegistry
name: BuildAndPushDockerBranch
identifier: BuildAndPushDockerBranch
spec:
connectorRef: Plugins_Docker_Hub_Connector
repo: plugins/kaniko<+matrix.image>
tags:
- linux-amd64
caching: false
dockerfile: docker/<+matrix.repo>/Dockerfile.linux.amd64
when:
stageStatus: Success
condition: <+codebase.build.type> == "branch"
strategy:
matrix:
image:
- ""
- "-gcr"
- "-gar"
- "-ecr"
- "-acr"
repo:
- docker
- gcr
- gar
- ecr
- acr
exclude:
- image: ""
repo: gcr
- image: ""
repo: gar
- image: ""
repo: ecr
- image: ""
repo: acr
- image: "-gcr"
repo: docker
- image: "-gcr"
repo: gar
- image: "-gcr"
repo: ecr
- image: "-gcr"
repo: acr
- image: "-gar"
repo: docker
- image: "-gar"
repo: gcr
- image: "-gar"
repo: ecr
- image: "-gar"
repo: acr
- image: "-ecr"
repo: docker
- image: "-ecr"
repo: gcr
- image: "-ecr"
repo: gar
- image: "-ecr"
repo: acr
- image: "-acr"
repo: docker
- image: "-acr"
repo: gcr
- image: "-acr"
repo: gar
- image: "-acr"
repo: ecr
nodeName: <+matrix.repo>
- step:
type: BuildAndPushDockerRegistry
name: BuildAndPushDockerBranch_Kaniko
identifier: BuildAndPushDockerBranch_Kaniko
spec:
connectorRef: Plugins_Docker_Hub_Connector
repo: plugins/kaniko<+matrix.image>
tags:
- linux-amd64-kaniko1.9.1
caching: false
dockerfile: docker/<+matrix.repo>/Dockerfile.linux.amd64.kaniko1.9.1
when:
stageStatus: Success
condition: <+codebase.build.type> == "branch"
strategy:
matrix:
image:
- ""
- "-gcr"
- "-gar"
- "-ecr"
repo:
- docker
- gcr
- gar
- ecr
exclude:
- image: ""
repo: gcr
- image: ""
repo: gar
- image: ""
repo: ecr
- image: "-gcr"
repo: docker
- image: "-gcr"
repo: gar
- image: "-gcr"
repo: ecr
- image: "-gar"
repo: docker
- image: "-gar"
repo: gcr
- image: "-gar"
repo: ecr
- image: "-ecr"
repo: docker
- image: "-ecr"
repo: gcr
- image: "-ecr"
repo: gar
nodeName: _<+matrix.repo>
when:
pipelineStatus: Success
- stage:
name: linux-arm64
identifier: linuxarm64
description: ""
type: CI
spec:
cloneCodebase: true
caching:
enabled: false
paths: []
platform:
os: Linux
arch: Arm64
runtime:
type: Cloud
spec: {}
execution:
steps:
- step:
type: Run
name: Build Binary
identifier: Build_and_Test
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: golang:1.23.0
shell: Sh
command: |-
go test ./...
sh scripts/build.sh
- parallel:
- step:
type: Plugin
name: BuildAndPushDockerTag
identifier: BuildAndPushDockerTag
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: plugins/docker
settings:
username: drone
password: <+secrets.getValue("Plugins_Docker_Hub_Pat")>
repo: plugins/kaniko<+matrix.image>
dockerfile: docker/<+matrix.repo>/Dockerfile.linux.arm64
auto_tag: "true"
auto_tag_suffix: linux-arm64
daemon_off: "false"
when:
stageStatus: Success
condition: <+codebase.build.type> == "tag"
strategy:
matrix:
image:
- ""
- "-gcr"
- "-gar"
- "-ecr"
- "-acr"
repo:
- docker
- gcr
- gar
- ecr
- acr
exclude:
- image: ""
repo: gcr
- image: ""
repo: gar
- image: ""
repo: ecr
- image: ""
repo: acr
- image: "-gcr"
repo: docker
- image: "-gcr"
repo: gar
- image: "-gcr"
repo: ecr
- image: "-gcr"
repo: acr
- image: "-gar"
repo: docker
- image: "-gar"
repo: gcr
- image: "-gar"
repo: ecr
- image: "-gar"
repo: acr
- image: "-ecr"
repo: docker
- image: "-ecr"
repo: gcr
- image: "-ecr"
repo: gar
- image: "-ecr"
repo: acr
- image: "-acr"
repo: docker
- image: "-acr"
repo: gcr
- image: "-acr"
repo: gar
- image: "-acr"
repo: ecr
nodeName: _<+matrix.repo>
- step:
type: Plugin
name: BuildAndPushDockerTag_Kaniko
identifier: BuildAndPushDockerTag_Kaniko
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: plugins/docker
settings:
username: drone
password: <+secrets.getValue("Plugins_Docker_Hub_Pat")>
repo: plugins/kaniko<+matrix.image>
dockerfile: docker/<+matrix.repo>/Dockerfile.linux.arm64.kaniko1.9.1
auto_tag: "true"
auto_tag_suffix: linux-arm64-kaniko1.9.1
daemon_off: "false"
when:
stageStatus: Success
condition: <+codebase.build.type> == "tag"
strategy:
matrix:
image:
- ""
- "-gcr"
- "-gar"
- "-ecr"
repo:
- docker
- gcr
- gar
- ecr
exclude:
- image: ""
repo: gcr
- image: ""
repo: gar
- image: ""
repo: ecr
- image: "-gcr"
repo: docker
- image: "-gcr"
repo: gar
- image: "-gcr"
repo: ecr
- image: "-gar"
repo: docker
- image: "-gar"
repo: gcr
- image: "-gar"
repo: ecr
- image: "-ecr"
repo: docker
- image: "-ecr"
repo: gcr
- image: "-ecr"
repo: gar
nodeName: _<+matrix.repo>
- parallel:
- step:
type: BuildAndPushDockerRegistry
name: BuildAndPushDockerBranch
identifier: BuildAndPushDockerBranch
spec:
connectorRef: Plugins_Docker_Hub_Connector
repo: plugins/kaniko<+matrix.image>
tags:
- linux-arm64
caching: false
dockerfile: docker/<+matrix.repo>/Dockerfile.linux.arm64
when:
stageStatus: Success
condition: <+codebase.build.type> == "branch"
strategy:
matrix:
image:
- ""
- "-gcr"
- "-gar"
- "-ecr"
- "-acr"
repo:
- docker
- gcr
- gar
- ecr
- acr
exclude:
- image: ""
repo: gcr
- image: ""
repo: gar
- image: ""
repo: ecr
- image: ""
repo: acr
- image: "-gcr"
repo: docker
- image: "-gcr"
repo: gar
- image: "-gcr"
repo: ecr
- image: "-gcr"
repo: acr
- image: "-gar"
repo: docker
- image: "-gar"
repo: gcr
- image: "-gar"
repo: ecr
- image: "-gar"
repo: acr
- image: "-ecr"
repo: docker
- image: "-ecr"
repo: gcr
- image: "-ecr"
repo: gar
- image: "-ecr"
repo: acr
- image: "-acr"
repo: docker
- image: "-acr"
repo: gcr
- image: "-acr"
repo: gar
- image: "-acr"
repo: ecr
nodeName: <+matrix.repo>
- step:
type: BuildAndPushDockerRegistry
name: BuildAndPushDockerBranch_Kaniko
identifier: BuildAndPushDockerBranch_Kaniko
spec:
connectorRef: Plugins_Docker_Hub_Connector
repo: plugins/kaniko<+matrix.image>
tags:
- linux-arm64-kaniko1.9.1
caching: false
dockerfile: docker/<+matrix.repo>/Dockerfile.linux.arm64.kaniko1.9.1
when:
stageStatus: Success
condition: <+codebase.build.type> == "branch"
strategy:
matrix:
image:
- ""
- "-gcr"
- "-gar"
- "-ecr"
repo:
- docker
- gcr
- gar
- ecr
exclude:
- image: ""
repo: gcr
- image: ""
repo: gar
- image: ""
repo: ecr
- image: "-gcr"
repo: docker
- image: "-gcr"
repo: gar
- image: "-gcr"
repo: ecr
- image: "-gar"
repo: docker
- image: "-gar"
repo: gcr
- image: "-gar"
repo: ecr
- image: "-ecr"
repo: docker
- image: "-ecr"
repo: gcr
- image: "-ecr"
repo: gar
nodeName: _<+matrix.repo>
when:
pipelineStatus: Success
- stage:
name: Manifest
identifier: Manifest
description: ""
type: CI
spec:
cloneCodebase: true
caching:
enabled: false
paths: []
platform:
os: Linux
arch: Amd64
runtime:
type: Cloud
spec: {}
execution:
steps:
- parallel:
- step:
type: Plugin
name: Manifest
identifier: Manifest
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: plugins/manifest
settings:
auto_tag: "true"
spec: docker/<+matrix.repo>/manifest.tmpl
username: drone
password: <+secrets.getValue("Plugins_Docker_Hub_Pat")>
ignore_missing: "true"
when:
stageStatus: Success
condition: <+codebase.build.type> == "branch" || <+codebase.build.type> == "tag"
strategy:
matrix:
repo:
- docker
- gcr
- gar
- ecr
- acr
nodeName: manifest_<+matrix.repo>
- step:
type: Plugin
name: Manifest_kaniko191
identifier: Manifest_kaniko
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: plugins/manifest
settings:
auto_tag: "false"
spec: docker/<+matrix.repo>/manifest-kaniko1.9.1.tmpl
username: drone
password: <+secrets.getValue("Plugins_Docker_Hub_Pat")>
ignore_missing: "true"
when:
stageStatus: Success
condition: <+codebase.build.type> == "branch" || <+codebase.build.type> == "tag"
strategy:
matrix:
repo:
- docker
- gcr
- gar
- ecr
nodeName: manifest_<+matrix.repo>
when:
pipelineStatus: Success
allowStageExecutions: true
+41 -3
View File
@@ -2,7 +2,14 @@
Drone kaniko plugin uses [kaniko](https://github.com/GoogleContainerTools/kaniko) to build and publish Docker images to a container registry.
Plugin images are published with 1.6.0 as well as 1.9.2 kaniko version from 1.5.1 release tag. `plugins/kaniko:<release-tag>` uses 1.6.0 version while `plugins/kaniko:<release-tag>-kaniko1.9.2` uses 1.9.2 version. Similar convention is used for plugins/kaniko-ecr & plugins/kaniko-gcr images as well.
Plugin images are published with 1.6.0 as well as 1.9.1 kaniko version from 1.5.1 release tag. `plugins/kaniko:<release-tag>` uses 1.6.0 version while `plugins/kaniko:<release-tag>-kaniko1.9.1` uses 1.9.1 version. Similar convention is used for plugins/kaniko-ecr & plugins/kaniko-gcr images as well.
Run the following script to install git-leaks support to this repo.
```
chmod +x ./git-hooks/install.sh
./git-hooks/install.sh
```
## Build
@@ -29,7 +36,7 @@ docker build \
--label org.label-schema.build-date=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
--label org.label-schema.vcs-ref=$(git rev-parse --short HEAD) \
--file docker/docker/Dockerfile.linux.amd64 --tag plugins/kaniko .
docker build \
--label org.label-schema.build-date=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
--label org.label-schema.vcs-ref=$(git rev-parse --short HEAD) \
@@ -47,6 +54,33 @@ docker build \
```
## Usage
### Operation Modes
Default Mode (Build and Push):
When neither `no_push` nor `push_only` is provided. Plugin builds and pushes the Docker image to a container registry.
Build-Only Mode (no-push):
When `no_push` is true and `destination_tar_path` is defined.
Plugin performs only the image build operation and saves the resulting image tarball to the specified `destination_tar_path`
It does not push the image to any registry.
Push-Only Mode (push-only):
When `push_only` is true and `source_tar_path` is defined.
Plugin loads an existing image tarball from the specified `source_tar_path`
and pushes the loaded image to a Container Registry.
It skips the build process.
### Mutually Exclusive Inputs
If both `no_push` and `push_only` inputs are provided, the plugin will:
Terminate the operation and
throw an error with the message: "Inputs no-push and push-only cannot be used together. Please define only one."
### Manual Tagging
```console
@@ -73,6 +107,7 @@ docker run --rm \
-w /drone \
plugins/kaniko:linux-amd64
```
would both be equivalent to
```
@@ -82,7 +117,7 @@ PLUGIN_TAGS=1,1.2,1.2.3,latest
This allows for passing `$DRONE_TAG` directly as a tag for repos that use [semver](https://semver.org) tags.
To avoid confusion between repo tags and image tags, `PLUGIN_EXPAND_TAG` also recognizes a semantic version
without the `v` prefix. As such, the following is also equivalent to the above:
without the `v` prefix. As such, the following is also equivalent to the above:
```console
docker run --rm \
@@ -94,6 +129,7 @@ docker run --rm \
```
### Auto Tagging
The [auto tag feature](https://plugins.drone.io/drone-plugins/drone-docker) of docker plugin is also supported.
When auto tagging is enabled, if any of the case is matched below, a docker build will be pushed with auto generated tags. Otherwise the docker build will be skipped.
@@ -115,6 +151,7 @@ docker run --rm \
```
Tags to push:
- 1.2.3
- 1.2
- 1
@@ -135,4 +172,5 @@ docker run --rm \
```
Tags to push:
- latest
+424 -48
View File
@@ -13,6 +13,8 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/crane"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
@@ -23,12 +25,11 @@ import (
)
const (
dockerPath string = "/kaniko/.docker"
clientIdEnv string = "AZURE_CLIENT_ID"
clientSecretKeyEnv string = "AZURE_CLIENT_SECRET"
dockerConfigPath string = "/kaniko/.docker"
tenantKeyEnv string = "AZURE_TENANT_ID"
certPathEnv string = "AZURE_CLIENT_CERTIFICATE_PATH"
dockerConfigPath string = "/kaniko/.docker"
defaultDigestFile string = "/kaniko/digest-file"
finalUrl string = "https://portal.azure.com/#view/Microsoft_Azure_ContainerRegistries/TagMetadataBlade/registryId/"
)
@@ -37,6 +38,7 @@ var (
ACRCertPath = "/kaniko/acr-cert.pem"
pluginVersion = "unknown"
username = "00000000-0000-0000-0000-000000000000"
maxPageCount = 1000 // maximum count of pages to cycle through before we break out
)
func main() {
@@ -121,6 +123,21 @@ func main() {
Usage: "ACR registry",
EnvVar: "PLUGIN_REGISTRY",
},
cli.StringFlag{
Name: "base-image-registry",
Usage: "Docker registry for base image",
EnvVar: "PLUGIN_DOCKER_REGISTRY,PLUGIN_BASE_IMAGE_REGISTRY,DOCKER_REGISTRY",
},
cli.StringFlag{
Name: "base-image-username",
Usage: "Docker username for base image registry",
EnvVar: "PLUGIN_DOCKER_USERNAME,PLUGIN_BASE_IMAGE_USERNAME,DOCKER_USERNAME",
},
cli.StringFlag{
Name: "base-image-password",
Usage: "Docker password for base image registry",
EnvVar: "PLUGIN_DOCKER_PASSWORD,PLUGIN_BASE_IMAGE_PASSWORD,DOCKER_PASSWORD",
},
cli.StringSliceFlag{
Name: "registry-mirrors",
Usage: "docker registry mirrors",
@@ -191,6 +208,21 @@ func main() {
Usage: "Set this flag if you only want to build the image, without pushing to a registry",
EnvVar: "PLUGIN_NO_PUSH",
},
cli.BoolFlag{
Name: "push-only",
Usage: "Set this flag if you only want to push a pre-built image from a tarball",
EnvVar: "PLUGIN_PUSH_ONLY",
},
cli.StringFlag{
Name: "source-tar-path",
Usage: "Path to the local tarball to be pushed when push-only is set",
EnvVar: "PLUGIN_SOURCE_TAR_PATH",
},
cli.StringFlag{
Name: "tar-path",
Usage: "Set this flag to save the image as a tarball at path",
EnvVar: "PLUGIN_TAR_PATH,PLUGIN_DESTINATION_TAR_PATH",
},
cli.StringFlag{
Name: "verbosity",
Usage: "Set this flag with value as oneof <panic|fatal|error|warn|info|debug|trace> to set the logging level for kaniko. Defaults to info.",
@@ -206,6 +238,157 @@ func main() {
Usage: "build only used stages",
EnvVar: "PLUGIN_SKIP_UNUSED_STAGES",
},
cli.StringFlag{
Name: "cache-dir",
Usage: "Set this flag to specify a local directory cache for base images",
EnvVar: "PLUGIN_CACHE_DIR",
},
cli.BoolFlag{
Name: "cache-copy-layers",
Usage: "Enable or disable copying layers from the cache.",
EnvVar: "PLUGIN_CACHE_COPY_LAYERS",
},
cli.BoolFlag{
Name: "cache-run-layers",
Usage: "Enable or disable running layers from the cache.",
EnvVar: "PLUGIN_CACHE_RUN_LAYERS",
},
cli.BoolFlag{
Name: "cleanup",
Usage: "Enable or disable cleanup of temporary files.",
EnvVar: "PLUGIN_CLEANUP",
},
cli.BoolFlag{
Name: "compressed-caching",
Usage: "Enable or disable compressed caching.",
EnvVar: "PLUGIN_COMPRESSED_CACHING",
},
cli.StringFlag{
Name: "context-sub-path",
Usage: "Sub-path within the context to build.",
EnvVar: "PLUGIN_CONTEXT_SUB_PATH",
},
cli.StringFlag{
Name: "custom-platform",
Usage: "Platform to use for building.",
EnvVar: "PLUGIN_CUSTOM_PLATFORM",
},
cli.BoolFlag{
Name: "force",
Usage: "Force building the image even if it already exists.",
EnvVar: "PLUGIN_FORCE",
},
cli.StringSliceFlag{
Name: "image-name-with-digest-file",
Usage: "Write image name with digest to a file.",
EnvVar: "PLUGIN_IMAGE_NAME_WITH_DIGEST_FILE",
},
cli.StringFlag{
Name: "image-name-tag-with-digest-file",
Usage: "Write image name with tag and digest to a file.",
EnvVar: "PLUGIN_IMAGE_NAME_TAG_WITH_DIGEST_FILE",
},
cli.BoolFlag{
Name: "insecure",
Usage: "Allow connecting to registries without TLS.",
EnvVar: "PLUGIN_INSECURE",
},
cli.BoolFlag{
Name: "insecure-pull",
Usage: "Allow insecure pulls from the registry.",
EnvVar: "PLUGIN_INSECURE_PULL",
},
cli.StringFlag{
Name: "insecure-registry",
Usage: "Use plain HTTP for registry communication.",
EnvVar: "PLUGIN_INSECURE_REGISTRY",
},
cli.StringFlag{
Name: "log-format",
Usage: "Set the log format for build output.",
EnvVar: "PLUGIN_LOG_FORMAT",
},
cli.BoolFlag{
Name: "log-timestamp",
Usage: "Show timestamps in build output.",
EnvVar: "PLUGIN_LOG_TIMESTAMP",
},
cli.StringFlag{
Name: "oci-layout-path",
Usage: "Directory to store OCI layout.",
EnvVar: "PLUGIN_OCI_LAYOUT_PATH",
},
cli.IntFlag{
Name: "push-retry",
Usage: "Number of times to retry pushing an image.",
EnvVar: "PLUGIN_PUSH_RETRY",
},
cli.StringFlag{
Name: "registry-certificate",
Usage: "Path to a file containing a registry certificate.",
EnvVar: "PLUGIN_REGISTRY_CERTIFICATE",
},
cli.StringFlag{
Name: "registry-client-cert",
Usage: "Path to a file containing a registry client certificate.",
EnvVar: "PLUGIN_REGISTRY_CLIENT_CERT",
},
cli.BoolFlag{
Name: "skip-default-registry-fallback",
Usage: "Skip Docker Hub and default registry fallback.",
EnvVar: "PLUGIN_SKIP_DEFAULT_REGISTRY_FALLBACK",
},
cli.BoolFlag{
Name: "reproducible",
Usage: "Create a reproducible image.",
EnvVar: "PLUGIN_REPRODUCIBLE",
},
cli.BoolFlag{
Name: "single-snapshot",
Usage: "Only create a single snapshot of the image.",
EnvVar: "PLUGIN_SINGLE_SNAPSHOT",
},
cli.BoolFlag{
Name: "skip-push-permission-check",
Usage: "Skip permission check when pushing.",
EnvVar: "PLUGIN_SKIP_PUSH_PERMISSION_CHECK",
},
cli.BoolFlag{
Name: "skip-tls-verify-pull",
Usage: "Skip TLS verification when pulling.",
EnvVar: "PLUGIN_SKIP_TLS_VERIFY_PULL",
},
cli.BoolFlag{
Name: "skip-tls-verify-registry",
Usage: "Skip TLS verification when connecting to a registry.",
EnvVar: "PLUGIN_SKIP_TLS_VERIFY_REGISTRY",
},
cli.BoolFlag{
Name: "use-new-run",
Usage: "Skip TLS verification when connecting to a registry.",
EnvVar: "PLUGIN_USE_NEW_RUN",
},
cli.BoolFlag{
Name: "ignore-var-run",
Usage: "Ignore the /var/run directory during build.",
EnvVar: "PLUGIN_IGNORE_VAR_RUN",
},
cli.StringFlag{
Name: "ignore-path",
Usage: "Path to ignore during the build.",
EnvVar: "PLUGIN_IGNORE_PATH",
},
cli.IntFlag{
Name: "image-fs-extract-retry",
Usage: "Number of retries for extracting filesystem layers.",
EnvVar: "PLUGIN_IMAGE_FS_EXTRACT_RETRY",
},
cli.IntFlag{
Name: "image-download-retry",
Usage: "Number of retries for downloading base images.",
EnvVar: "PLUGIN_IMAGE_DOWNLOAD_RETRY",
},
}
if err := app.Run(os.Args); err != nil {
@@ -214,6 +397,11 @@ func main() {
}
func run(c *cli.Context) error {
// Check if push-only flag is set
if c.Bool("push-only") {
return handlePushOnly(c)
}
registry := c.String("registry")
noPush := c.Bool("no-push")
@@ -224,6 +412,9 @@ func run(c *cli.Context) error {
c.String("client-secret"),
c.String("subscription-id"),
registry,
c.String("base-image-username"),
c.String("base-image-password"),
c.String("base-image-registry"),
noPush,
)
if err != nil {
@@ -232,28 +423,59 @@ func run(c *cli.Context) error {
plugin := kaniko.Plugin{
Build: kaniko.Build{
DroneCommitRef: c.String("drone-commit-ref"),
DroneRepoBranch: c.String("drone-repo-branch"),
Dockerfile: c.String("dockerfile"),
Context: c.String("context"),
Tags: c.StringSlice("tags"),
AutoTag: c.Bool("auto-tag"),
AutoTagSuffix: c.String("auto-tag-suffix"),
ExpandTag: c.Bool("expand-tag"),
Args: c.StringSlice("args"),
Target: c.String("target"),
Repo: c.String("repo"),
Mirrors: c.StringSlice("registry-mirrors"),
Labels: c.StringSlice("custom-labels"),
SnapshotMode: c.String("snapshot-mode"),
EnableCache: c.Bool("enable-cache"),
CacheRepo: fmt.Sprintf("%s/%s", c.String("registry"), c.String("cache-repo")),
CacheTTL: c.Int("cache-ttl"),
DigestFile: defaultDigestFile,
NoPush: noPush,
Verbosity: c.String("verbosity"),
Platform: c.String("platform"),
SkipUnusedStages: c.Bool("skip-unused-stages"),
DroneCommitRef: c.String("drone-commit-ref"),
DroneRepoBranch: c.String("drone-repo-branch"),
Dockerfile: c.String("dockerfile"),
Context: c.String("context"),
Tags: c.StringSlice("tags"),
AutoTag: c.Bool("auto-tag"),
AutoTagSuffix: c.String("auto-tag-suffix"),
ExpandTag: c.Bool("expand-tag"),
Args: c.StringSlice("args"),
Target: c.String("target"),
Repo: c.String("repo"),
Mirrors: c.StringSlice("registry-mirrors"),
Labels: c.StringSlice("custom-labels"),
SnapshotMode: c.String("snapshot-mode"),
EnableCache: c.Bool("enable-cache"),
CacheRepo: fmt.Sprintf("%s/%s", c.String("registry"), c.String("cache-repo")),
CacheTTL: c.Int("cache-ttl"),
DigestFile: defaultDigestFile,
NoPush: noPush,
Verbosity: c.String("verbosity"),
Platform: c.String("platform"),
SkipUnusedStages: c.Bool("skip-unused-stages"),
CacheDir: c.String("cache-dir"),
CacheCopyLayers: c.Bool("cache-copy-layers"),
CacheRunLayers: c.Bool("cache-run-layers"),
Cleanup: c.Bool("cleanup"),
ContextSubPath: c.String("context-sub-path"),
CustomPlatform: c.String("custom-platform"),
Force: c.Bool("force"),
ImageNameWithDigestFile: c.String("image-name-with-digest-file"),
ImageNameTagWithDigestFile: c.String("image-name-tag-with-digest-file"),
Insecure: c.Bool("insecure"),
InsecurePull: c.Bool("insecure-pull"),
InsecureRegistry: c.String("insecure-registry"),
Label: c.String("label"),
LogFormat: c.String("log-format"),
LogTimestamp: c.Bool("log-timestamp"),
OCILayoutPath: c.String("oci-layout-path"),
PushRetry: c.Int("push-retry"),
RegistryCertificate: c.String("registry-certificate"),
RegistryClientCert: c.String("registry-client-cert"),
SkipDefaultRegistryFallback: c.Bool("skip-default-registry-fallback"),
Reproducible: c.Bool("reproducible"),
SingleSnapshot: c.Bool("single-snapshot"),
SkipTLSVerify: c.Bool("skip-tls-verify"),
SkipPushPermissionCheck: c.Bool("skip-push-permission-check"),
SkipTLSVerifyPull: c.Bool("skip-tls-verify-pull"),
SkipTLSVerifyRegistry: c.Bool("skip-tls-verify-registry"),
UseNewRun: c.Bool("use-new-run"),
IgnorePath: c.String("ignore-path"),
IgnorePaths: c.StringSlice("ignore-paths"),
ImageFSExtractRetry: c.Int("image-fs-extract-retry"),
ImageDownloadRetry: c.Int("image-download-retry"),
},
Artifact: kaniko.Artifact{
Tags: c.StringSlice("tags"),
@@ -263,33 +485,55 @@ func run(c *cli.Context) error {
RegistryType: artifact.Docker,
},
}
if c.IsSet("compressed-caching") {
flag := c.Bool("compressed-caching")
plugin.Build.CompressedCaching = &flag
}
if c.IsSet("ignore-var-run") {
flag := c.Bool("ignore-var-run")
plugin.Build.IgnoreVarRun = &flag
}
// Set tar-path if provided
if c.IsSet("tar-path") {
plugin.Build.TarPath = c.String("tar-path")
}
return plugin.Exec()
}
func setupAuth(tenantId, clientId, cert,
clientSecret, subscriptionId, registry string, noPush bool) (string, error) {
clientSecret, subscriptionId, registry, dockerUsername, dockerPassword, dockerRegistry string, noPush bool) (string, error) {
if registry == "" {
return "", fmt.Errorf("registry must be specified")
}
if noPush {
return "", nil
}
// case of client secret or cert based auth
if clientId != "" {
// only setup auth when pushing or credentials are defined
token, publicUrl, err := getACRToken(subscriptionId, tenantId, clientId, clientSecret, cert, registry)
if err != nil {
if noPush {
logrus.Warnf("NO_PUSH mode: failed to fetch ACR Token: %v", err)
return "", nil
}
return "", errors.Wrap(err, "failed to fetch ACR Token")
}
err = docker.CreateDockerCfgFile(username, token, registry, dockerConfigPath)
if err != nil {
// setup docker config for azure registry and base image docker registry
if err := setDockerAuth(username, token, registry, dockerUsername, dockerPassword, dockerRegistry); err != nil {
if noPush {
logrus.Warnf("NO_PUSH mode: failed to create docker config: %v", err)
return "", nil
}
return "", errors.Wrap(err, "failed to create docker config")
}
return publicUrl, nil
} else {
if noPush {
return "", nil
}
return "", fmt.Errorf("managed authentication is not supported")
}
}
@@ -407,40 +651,172 @@ func getPublicUrl(token, registryUrl, subscriptionId string) (string, error) {
}
registry := strings.Split(registryUrl, ".")[0]
burl := "https://management.azure.com/subscriptions/" +
baseURL := "https://management.azure.com/subscriptions/" +
subscriptionId + "/resources?$filter=resourceType%20eq%20'Microsoft.ContainerRegistry/registries'%20and%20name%20eq%20'" +
registry + "'&api-version=2021-04-01&$select=id"
method := "GET"
client := &http.Client{}
req, err := http.NewRequest(method, burl, nil)
if err != nil {
fmt.Println(err)
return "", errors.Wrap(err, "failed to create request for getting container registry setting")
cnt := 0
for {
// this is just in case we end up cycling through nextLink's infinitely.
// this should not happen - added as a precaution.
if cnt > maxPageCount {
break
}
cnt++
req, err := http.NewRequest(method, baseURL, nil)
if err != nil {
return "", errors.Wrap(err, "failed to create request for getting container registry setting")
}
req.Header.Add("Authorization", "Bearer "+token)
res, err := client.Do(req)
if err != nil {
return "", errors.Wrap(err, "failed to send request for getting container registry setting")
}
defer res.Body.Close()
var response strct
err = json.NewDecoder(res.Body).Decode(&response)
if err != nil {
return "", errors.Wrap(err, "failed to send request for getting container registry setting")
}
if len(response.Value) > 0 {
if response.Value[0].ID == "" { // should not happen
return "", errors.New("received empty registry ID from /subscriptions API")
}
return finalUrl + encodeParam(response.Value[0].ID), nil
}
if response.NextLink == "" {
// No more pages, break the loop
break
}
baseURL = response.NextLink
}
req.Header.Add("Authorization", "Bearer "+token)
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return "", errors.Wrap(err, "failed to send request for getting container registry setting")
}
defer res.Body.Close()
return "", errors.New("did not receive any registry information from /subscriptions API")
}
var response strct
err = json.NewDecoder(res.Body).Decode(&response)
if err != nil {
return "", errors.Wrap(err, "failed to send request for getting container registry setting")
func setDockerAuth(username, password, registry, dockerUsername, dockerPassword, dockerRegistry string) error {
dockerConfig := docker.NewConfig()
pushToRegistryCreds := docker.RegistryCredentials{
Registry: registry,
Username: username,
Password: password,
}
return finalUrl + encodeParam(response.Value[0].ID), nil
pullFromRegistryCreds := docker.RegistryCredentials{
Registry: dockerRegistry,
Username: dockerUsername,
Password: dockerPassword,
}
credentials := []docker.RegistryCredentials{pushToRegistryCreds, pullFromRegistryCreds}
return dockerConfig.CreateDockerConfig(credentials, dockerConfigPath)
}
func encodeParam(s string) string {
return url.QueryEscape(s)
}
func handlePushOnly(c *cli.Context) error {
// Validate inputs for push-only operation
sourceTarPath := c.String("source-tar-path")
if sourceTarPath == "" {
return fmt.Errorf("source_tar_path is required when push_only is set")
}
if _, err := os.Stat(sourceTarPath); os.IsNotExist(err) {
return fmt.Errorf("image tarball does not exist at path: %s", sourceTarPath)
}
repo := c.String("repo")
registry := c.String("registry")
if repo == "" || registry == "" {
return fmt.Errorf("repository and registry must be specified for push-only operation")
}
// Setup ACR authentication
publicUrl, err := setupAuth(
c.String("tenant-id"),
c.String("client-id"),
c.String("client-cert"),
c.String("client-secret"),
c.String("subscription-id"),
registry,
c.String("base-image-username"),
c.String("base-image-password"),
c.String("base-image-registry"),
false, // We want to push in push-only mode
)
if err != nil {
return err
}
// Load the image from the tarball
logrus.Infof("Loading image from tarball: %s", sourceTarPath)
img, err := crane.Load(sourceTarPath)
if err != nil {
return fmt.Errorf("failed to load image from tarball: %v", err)
}
// Check if the Docker config directory exists (should have been created by setupAuth)
if _, err := os.Stat(dockerConfigPath); os.IsNotExist(err) {
return fmt.Errorf("Docker config directory does not exist: %v", err)
} else if err != nil {
return fmt.Errorf("error checking Docker config directory: %v", err)
}
// Explicitly set DOCKER_CONFIG environment variable to ensure crane finds the config
if err := os.Setenv("DOCKER_CONFIG", dockerConfigPath); err != nil {
return fmt.Errorf("failed to set DOCKER_CONFIG environment variable: %v", err)
}
// Setup crane options
opts := []crane.Option{
crane.WithAuthFromKeychain(authn.DefaultKeychain),
}
// Push for each tag
tags := c.StringSlice("tags")
if len(tags) == 0 {
tags = []string{"latest"}
}
// Use the registry from setupAuth if publicUrl is available, otherwise use the provided registry
pushRegistry := registry
if publicUrl != "" {
logrus.Infof("Using public URL for pushing: %s", publicUrl)
// Extract just the registry part from the full URL if needed
// This depends on the format of publicUrl, adjust parsing as needed
pushRegistry = publicUrl
}
for _, tag := range tags {
dest := fmt.Sprintf("%s/%s:%s", pushRegistry, repo, tag)
logrus.Infof("Pushing image to: %s", dest)
if err := crane.Push(img, dest, opts...); err != nil {
return fmt.Errorf("failed to push image to %s: %v", dest, err)
}
logrus.Infof("Successfully pushed image to %s", dest)
}
return nil
}
type strct struct {
Value []struct {
ID string `json:"id"`
} `json:"value"`
NextLink string `json:"nextLink"` // for pagination
}
+156
View File
@@ -0,0 +1,156 @@
package main
import (
"encoding/base64"
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/drone/drone-kaniko/pkg/docker"
"github.com/stretchr/testify/assert"
)
const (
v2RegistryURL string = "https://index.docker.io/v2/" // v2 registry is not supported
)
func TestCreateDockerConfigWithBaseRegistry(t *testing.T) {
username := "user1"
password := "pass1"
registry := "azurecr.io"
dockerUsername := "dockeruser"
dockerPassword := "dockerpass"
dockerRegistry := "https://index.docker.io/v1/"
privateRegistry := "privateDockerRegistry"
privateRegistryUsername := "priaveUsername"
privateRegistryPassword := "privatePassword"
credentials := []docker.RegistryCredentials{
{
Registry: registry,
Username: username,
Password: password,
},
{
Registry: dockerRegistry,
Username: dockerUsername,
Password: dockerPassword,
},
{
Registry: privateRegistry,
Username: privateRegistryUsername,
Password: privateRegistryPassword,
},
}
tempDir, err := ioutil.TempDir("", "docker-config-test")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)
config := docker.NewConfig()
err = config.CreateDockerConfig(credentials, tempDir)
assert.NoError(t, err)
expectedAuth := docker.Auth{Auth: base64.StdEncoding.EncodeToString([]byte(username + ":" + password))}
assert.Equal(t, expectedAuth, config.Auths[registry])
expectedDockerAuth := docker.Auth{Auth: base64.StdEncoding.EncodeToString([]byte(dockerUsername + ":" + dockerPassword))}
assert.Equal(t, expectedDockerAuth, config.Auths[dockerRegistry])
configPath := filepath.Join(tempDir, "config.json")
data, err := ioutil.ReadFile(configPath)
assert.NoError(t, err)
var configFromFile docker.Config
err = json.Unmarshal(data, &configFromFile)
assert.NoError(t, err)
assert.Equal(t, config.Auths, configFromFile.Auths)
err = config.CreateDockerConfig([]docker.RegistryCredentials{
{
Registry: registry,
Username: "",
Password: password,
},
}, tempDir)
assert.EqualError(t, err, "Username must be specified for registry: "+registry)
err = config.CreateDockerConfig([]docker.RegistryCredentials{
{
Registry: registry,
Username: username,
Password: "",
},
}, tempDir)
assert.EqualError(t, err, "Password must be specified for registry: "+registry)
// v1 registry but without username password
err = config.CreateDockerConfig([]docker.RegistryCredentials{
{
Registry: registry,
Username: username,
Password: password,
},
{
Registry: dockerRegistry,
Username: "",
Password: "",
},
}, tempDir)
assert.EqualError(t, err, "Username must be specified for registry: "+dockerRegistry)
// private base registry without username/password
err = config.CreateDockerConfig([]docker.RegistryCredentials{
{
Registry: privateRegistry,
Username: "",
Password: "",
},
}, tempDir)
assert.EqualError(t, err, "Username must be specified for registry: "+privateRegistry)
}
func TestCreateDockerConfigWithoutBaseRegistry(t *testing.T) {
username := "user1"
password := "pass1"
registry := "azurecr.io"
credentials := []docker.RegistryCredentials{
{
Registry: registry,
Username: username,
Password: password,
},
}
// Create a temporary directory
tempDir, err := ioutil.TempDir("", "docker-config-test")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)
config := docker.NewConfig()
err = config.CreateDockerConfig(credentials, tempDir)
assert.NoError(t, err)
expectedAuth := docker.Auth{Auth: base64.StdEncoding.EncodeToString([]byte(username + ":" + password))}
assert.Equal(t, expectedAuth, config.Auths[registry])
// Check the contents of the config.json file
configPath := filepath.Join(tempDir, "config.json")
data, err := ioutil.ReadFile(configPath)
assert.NoError(t, err)
var configFromFile docker.Config
err = json.Unmarshal(data, &configFromFile)
assert.NoError(t, err)
assert.Equal(t, config.Auths, configFromFile.Auths)
// Check if the public Docker Hub auth is not set
_, exists := config.Auths[""]
assert.False(t, exists)
}
+282 -76
View File
@@ -1,9 +1,6 @@
package main
import (
"encoding/base64"
"fmt"
"io/ioutil"
"os"
"strings"
@@ -14,6 +11,7 @@ import (
kaniko "github.com/drone/drone-kaniko"
"github.com/drone/drone-kaniko/pkg/artifact"
"github.com/drone/drone-kaniko/pkg/docker"
)
const (
@@ -21,9 +19,7 @@ const (
dockerPath string = "/kaniko/.docker"
dockerConfigPath string = "/kaniko/.docker/config.json"
v1RegistryURL string = "https://index.docker.io/v1/" // Default registry
v2RegistryURL string = "https://index.docker.io/v2/" // v2 registry is not supported
v2HubRegistryURL string = "https://registry.hub.docker.com/v2/"
v1RegistryURL string = "https://index.docker.io/v1/" // Default registry
defaultDigestFile string = "/kaniko/digest-file"
)
@@ -122,10 +118,15 @@ func main() {
},
cli.StringFlag{
Name: "registry",
Usage: "docker registry",
Usage: "docker registry of registry to push image to",
Value: v1RegistryURL,
EnvVar: "PLUGIN_REGISTRY",
},
cli.StringFlag{
Name: "base-image-registry",
Usage: "Docker registry for base image",
EnvVar: "PLUGIN_DOCKER_REGISTRY,PLUGIN_BASE_IMAGE_REGISTRY,DOCKER_REGISTRY",
},
cli.StringSliceFlag{
Name: "registry-mirrors",
Usage: "docker registry mirrors",
@@ -133,14 +134,24 @@ func main() {
},
cli.StringFlag{
Name: "username",
Usage: "docker username",
Usage: "docker username of registry to push image to",
EnvVar: "PLUGIN_USERNAME",
},
cli.StringFlag{
Name: "base-image-username",
Usage: "Docker username for base image registry",
EnvVar: "PLUGIN_DOCKER_USERNAME,PLUGIN_BASE_IMAGE_USERNAME,DOCKER_USERNAME",
},
cli.StringFlag{
Name: "password",
Usage: "docker password",
Usage: "docker password of registry to push image to",
EnvVar: "PLUGIN_PASSWORD",
},
cli.StringFlag{
Name: "base-image-password",
Usage: "Docker password for base image registry",
EnvVar: "PLUGIN_DOCKER_PASSWORD,PLUGIN_BASE_IMAGE_PASSWORD,DOCKER_PASSWORD",
},
cli.BoolFlag{
Name: "skip-tls-verify",
Usage: "Skip registry tls verify",
@@ -179,7 +190,7 @@ func main() {
cli.StringFlag{
Name: "tar-path",
Usage: "Set this flag to save the image as a tarball at path",
EnvVar: "PLUGIN_TAR_PATH",
EnvVar: "PLUGIN_TAR_PATH, PLUGIN_DESTINATION_TAR_PATH",
},
cli.StringFlag{
Name: "verbosity",
@@ -201,6 +212,172 @@ func main() {
Usage: "Output file location that will be generated by the plugin. This file will include information of the output that are exported by the plugin.",
EnvVar: "DRONE_OUTPUT",
},
cli.StringFlag{
Name: "cache-dir",
Usage: "Set this flag to specify a local directory cache for base images",
EnvVar: "PLUGIN_CACHE_DIR",
},
cli.BoolFlag{
Name: "cache-copy-layers",
Usage: "Enable or disable copying layers from the cache.",
EnvVar: "PLUGIN_CACHE_COPY_LAYERS",
},
cli.BoolFlag{
Name: "cache-run-layers",
Usage: "Enable or disable running layers from the cache.",
EnvVar: "PLUGIN_CACHE_RUN_LAYERS",
},
cli.BoolFlag{
Name: "cleanup",
Usage: "Enable or disable cleanup of temporary files.",
EnvVar: "PLUGIN_CLEANUP",
},
cli.BoolFlag{
Name: "compressed-caching",
Usage: "Enable or disable compressed caching.",
EnvVar: "PLUGIN_COMPRESSED_CACHING",
},
cli.StringFlag{
Name: "context-sub-path",
Usage: "Sub-path within the context to build.",
EnvVar: "PLUGIN_CONTEXT_SUB_PATH",
},
cli.StringFlag{
Name: "custom-platform",
Usage: "Platform to use for building.",
EnvVar: "PLUGIN_CUSTOM_PLATFORM",
},
cli.BoolFlag{
Name: "force",
Usage: "Force building the image even if it already exists.",
EnvVar: "PLUGIN_FORCE",
},
cli.StringFlag{
Name: "image-name-with-digest-file",
Usage: "Write image name with digest to a file.",
EnvVar: "PLUGIN_IMAGE_NAME_WITH_DIGEST_FILE",
},
cli.StringFlag{
Name: "image-name-tag-with-digest-file",
Usage: "Write image name with tag and digest to a file.",
EnvVar: "PLUGIN_IMAGE_NAME_TAG_WITH_DIGEST_FILE",
},
cli.BoolFlag{
Name: "insecure",
Usage: "Allow connecting to registries without TLS.",
EnvVar: "PLUGIN_INSECURE",
},
cli.BoolFlag{
Name: "insecure-pull",
Usage: "Allow insecure pulls from the registry.",
EnvVar: "PLUGIN_INSECURE_PULL",
},
cli.StringFlag{
Name: "insecure-registry",
Usage: "Use plain HTTP for registry communication.",
EnvVar: "PLUGIN_INSECURE_REGISTRY",
},
cli.StringFlag{
Name: "log-format",
Usage: "Set the log format for build output.",
EnvVar: "PLUGIN_LOG_FORMAT",
},
cli.BoolFlag{
Name: "log-timestamp",
Usage: "Show timestamps in build output.",
EnvVar: "PLUGIN_LOG_TIMESTAMP",
},
cli.StringFlag{
Name: "oci-layout-path",
Usage: "Directory to store OCI layout.",
EnvVar: "PLUGIN_OCI_LAYOUT_PATH",
},
cli.IntFlag{
Name: "push-retry",
Usage: "Number of times to retry pushing an image.",
EnvVar: "PLUGIN_PUSH_RETRY",
},
cli.StringFlag{
Name: "registry-certificate",
Usage: "Path to a file containing a registry certificate.",
EnvVar: "PLUGIN_REGISTRY_CERTIFICATE",
},
cli.StringFlag{
Name: "registry-client-cert",
Usage: "Path to a file containing a registry client certificate.",
EnvVar: "PLUGIN_REGISTRY_CLIENT_CERT",
},
cli.BoolFlag{
Name: "skip-default-registry-fallback",
Usage: "Skip Docker Hub and default registry fallback.",
EnvVar: "PLUGIN_SKIP_DEFAULT_REGISTRY_FALLBACK",
},
cli.BoolFlag{
Name: "reproducible",
Usage: "Create a reproducible image.",
EnvVar: "PLUGIN_REPRODUCIBLE",
},
cli.BoolFlag{
Name: "single-snapshot",
Usage: "Only create a single snapshot of the image.",
EnvVar: "PLUGIN_SINGLE_SNAPSHOT",
},
cli.BoolFlag{
Name: "skip-push-permission-check",
Usage: "Skip permission check when pushing.",
EnvVar: "PLUGIN_SKIP_PUSH_PERMISSION_CHECK",
},
cli.BoolFlag{
Name: "skip-tls-verify-pull",
Usage: "Skip TLS verification when pulling.",
EnvVar: "PLUGIN_SKIP_TLS_VERIFY_PULL",
},
cli.BoolFlag{
Name: "skip-tls-verify-registry",
Usage: "Skip TLS verification when connecting to a registry.",
EnvVar: "PLUGIN_SKIP_TLS_VERIFY_REGISTRY",
},
cli.BoolFlag{
Name: "use-new-run",
Usage: "Skip TLS verification when connecting to a registry.",
EnvVar: "PLUGIN_USE_NEW_RUN",
},
cli.BoolFlag{
Name: "ignore-var-run",
Usage: "Ignore the /var/run directory during build.",
EnvVar: "PLUGIN_IGNORE_VAR_RUN",
},
cli.StringFlag{
Name: "ignore-path",
Usage: "Path to ignore during the build.",
EnvVar: "PLUGIN_IGNORE_PATH",
},
cli.StringSliceFlag{
Name: "ignore-paths",
Usage: "Path to ignore during the build.",
EnvVar: "PLUGIN_IGNORE_PATHS",
},
cli.IntFlag{
Name: "image-fs-extract-retry",
Usage: "Number of retries for extracting filesystem layers.",
EnvVar: "PLUGIN_IMAGE_FS_EXTRACT_RETRY",
},
cli.IntFlag{
Name: "image-download-retry",
Usage: "Number of retries for downloading base images.",
EnvVar: "PLUGIN_IMAGE_DOWNLOAD_RETRY",
},
cli.StringFlag{
Name: "source-tar-path",
Usage: "Set this flag for the source tarball during push operations.",
EnvVar: "PLUGIN_SOURCE_TAR_PATH",
},
cli.BoolFlag{
Name: "push-only",
Usage: "Specify if the operation is push-only",
EnvVar: "PLUGIN_PUSH_ONLY",
},
}
if err := app.Run(os.Args); err != nil {
@@ -212,45 +389,86 @@ func run(c *cli.Context) error {
username := c.String("username")
noPush := c.Bool("no-push")
configOverride := c.String("dockerconfig")
// if configOverride is provided, use this for docker auth
// if configOverride is provided, use this directly to write to docker config file
if len(configOverride) > 0 {
if err := writeDockerCfgFile([]byte(configOverride)); err != nil {
if err := docker.WriteDockerConfig([]byte(configOverride), dockerPath); err != nil {
return err
}
} else if !noPush || username != "" {
// setup auth when pushing or credentials are defined and docker config override is false
if err := createDockerCfgFile(username, c.String("password"), c.String("registry")); err != nil {
return err
// setup auth when pushing/pulling or credentials are defined and docker config override is false
err := setDockerAuth(
c.String("username"),
c.String("password"),
c.String("registry"),
c.String("base-image-username"),
c.String("base-image-password"),
c.String("base-image-registry"),
)
if err != nil {
return errors.Wrap(err, "failed to create docker config")
}
}
plugin := kaniko.Plugin{
Build: kaniko.Build{
DroneCommitRef: c.String("drone-commit-ref"),
DroneRepoBranch: c.String("drone-repo-branch"),
Dockerfile: c.String("dockerfile"),
Context: c.String("context"),
Tags: c.StringSlice("tags"),
AutoTag: c.Bool("auto-tag"),
AutoTagSuffix: c.String("auto-tag-suffix"),
ExpandTag: c.Bool("expand-tag"),
Args: c.StringSlice("args"),
Target: c.String("target"),
Repo: buildRepo(c.String("registry"), c.String("repo"), c.Bool("expand-repo")),
Mirrors: c.StringSlice("registry-mirrors"),
Labels: c.StringSlice("custom-labels"),
SkipTlsVerify: c.Bool("skip-tls-verify"),
SnapshotMode: c.String("snapshot-mode"),
EnableCache: c.Bool("enable-cache"),
CacheRepo: buildRepo(c.String("registry"), c.String("cache-repo"), c.Bool("expand-repo")),
CacheTTL: c.Int("cache-ttl"),
DigestFile: defaultDigestFile,
NoPush: noPush,
TarPath: c.String("tar-path"),
Verbosity: c.String("verbosity"),
Platform: c.String("platform"),
SkipUnusedStages: c.Bool("skip-unused-stages"),
DroneCommitRef: c.String("drone-commit-ref"),
DroneRepoBranch: c.String("drone-repo-branch"),
Dockerfile: c.String("dockerfile"),
Context: c.String("context"),
Tags: c.StringSlice("tags"),
AutoTag: c.Bool("auto-tag"),
AutoTagSuffix: c.String("auto-tag-suffix"),
ExpandTag: c.Bool("expand-tag"),
Args: c.StringSlice("args"),
Target: c.String("target"),
Repo: buildRepo(c.String("registry"), c.String("repo"), c.Bool("expand-repo")),
Mirrors: c.StringSlice("registry-mirrors"),
Labels: c.StringSlice("custom-labels"),
SkipTlsVerify: c.Bool("skip-tls-verify"),
SnapshotMode: c.String("snapshot-mode"),
EnableCache: c.Bool("enable-cache"),
CacheRepo: buildRepo(c.String("registry"), c.String("cache-repo"), c.Bool("expand-repo")),
CacheTTL: c.Int("cache-ttl"),
DigestFile: defaultDigestFile,
NoPush: noPush,
TarPath: c.String("tar-path"),
Verbosity: c.String("verbosity"),
Platform: c.String("platform"),
PushOnly: c.Bool("push-only"),
SkipUnusedStages: c.Bool("skip-unused-stages"),
CacheDir: c.String("cache-dir"),
CacheCopyLayers: c.Bool("cache-copy-layers"),
CacheRunLayers: c.Bool("cache-run-layers"),
Cleanup: c.Bool("cleanup"),
ContextSubPath: c.String("context-sub-path"),
CustomPlatform: c.String("custom-platform"),
Force: c.Bool("force"),
ImageNameWithDigestFile: c.String("image-name-with-digest-file"),
ImageNameTagWithDigestFile: c.String("image-name-tag-with-digest-file"),
Insecure: c.Bool("insecure"),
InsecurePull: c.Bool("insecure-pull"),
InsecureRegistry: c.String("insecure-registry"),
Label: c.String("label"),
LogFormat: c.String("log-format"),
LogTimestamp: c.Bool("log-timestamp"),
OCILayoutPath: c.String("oci-layout-path"),
PushRetry: c.Int("push-retry"),
RegistryCertificate: c.String("registry-certificate"),
RegistryClientCert: c.String("registry-client-cert"),
SkipDefaultRegistryFallback: c.Bool("skip-default-registry-fallback"),
Reproducible: c.Bool("reproducible"),
SingleSnapshot: c.Bool("single-snapshot"),
SkipTLSVerify: c.Bool("skip-tls-verify"),
SkipPushPermissionCheck: c.Bool("skip-push-permission-check"),
SkipTLSVerifyPull: c.Bool("skip-tls-verify-pull"),
SkipTLSVerifyRegistry: c.Bool("skip-tls-verify-registry"),
SourceTarPath: c.String("source-tar-path"),
UseNewRun: c.Bool("use-new-run"),
IgnorePath: c.String("ignore-path"),
IgnorePaths: c.StringSlice("ignore-paths"),
ImageFSExtractRetry: c.Int("image-fs-extract-retry"),
ImageDownloadRetry: c.Int("image-download-retry"),
},
Artifact: kaniko.Artifact{
Tags: c.StringSlice("tags"),
@@ -263,48 +481,36 @@ func run(c *cli.Context) error {
OutputFile: c.String("output-file"),
},
}
if c.IsSet("compressed-caching") {
flag := c.Bool("compressed-caching")
plugin.Build.CompressedCaching = &flag
}
if c.IsSet("ignore-var-run") {
flag := c.Bool("ignore-var-run")
plugin.Build.IgnoreVarRun = &flag
}
return plugin.Exec()
}
// Create the docker config file for authentication
func createDockerCfgFile(username, password, registry string) error {
if username == "" {
return fmt.Errorf("Username must be specified")
}
if password == "" {
return fmt.Errorf("Password must be specified")
}
if registry == "" {
return fmt.Errorf("Registry must be specified")
func setDockerAuth(username, password, registry, baseImageUsername, baseImagePassword, baseImageRegistry string) error {
dockerConfig := docker.NewConfig()
pushToRegistryCreds := docker.RegistryCredentials{
Registry: registry,
Username: username,
Password: password,
}
credentials := []docker.RegistryCredentials{pushToRegistryCreds}
if registry == v2RegistryURL || registry == v2HubRegistryURL {
fmt.Println("Docker v2 registry is not supported in kaniko. Refer issue: https://github.com/GoogleContainerTools/kaniko/issues/1209")
fmt.Printf("Using v1 registry instead: %s\n", v1RegistryURL)
registry = v1RegistryURL
if baseImageRegistry != "" {
pullFromRegistryCreds := docker.RegistryCredentials{
Registry: baseImageRegistry,
Username: baseImageUsername,
Password: baseImagePassword,
}
credentials = append(credentials, pullFromRegistryCreds)
}
authBytes := []byte(fmt.Sprintf("%s:%s", username, password))
encodedString := base64.StdEncoding.EncodeToString(authBytes)
jsonBytes := []byte(fmt.Sprintf(`{"auths": {"%s": {"auth": "%s"}}}`, registry, encodedString))
if err := writeDockerCfgFile(jsonBytes); err != nil {
return errors.Wrap(err, "failed to write docker config file")
}
return nil
}
// Write json bytes in the docker config file
func writeDockerCfgFile(jsonBytes []byte) error {
err := os.MkdirAll(dockerPath, 0600)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to create %s directory", dockerPath))
}
err = ioutil.WriteFile(dockerConfigPath, jsonBytes, 0644)
if err != nil {
return errors.Wrap(err, "failed to create docker config file")
}
return nil
// Creates docker config for both the regustries used for authentication
return dockerConfig.CreateDockerConfig(credentials, dockerPath)
}
func buildRepo(registry, repo string, expandRepo bool) string {
+87 -1
View File
@@ -1,6 +1,12 @@
package main
import "testing"
import (
"io/ioutil"
"os"
"testing"
"github.com/drone/drone-kaniko/pkg/docker"
)
func Test_buildRepo(t *testing.T) {
tests := []struct {
@@ -35,3 +41,83 @@ func Test_buildRepo(t *testing.T) {
})
}
}
func TestCreateDockerConfig(t *testing.T) {
config := docker.NewConfig()
tempDir, err := ioutil.TempDir("", "docker-config-test")
if err != nil {
t.Fatalf("Failed to create temporary directory: %v", err)
}
defer os.RemoveAll(tempDir)
tests := []struct {
name string
credentials []docker.RegistryCredentials
wantErr bool
}{
{
name: "valid credentials",
credentials: []docker.RegistryCredentials{
{
Registry: "https://index.docker.io/v1/",
Username: "testuser",
Password: "testpassword",
},
},
wantErr: false,
},
{
name: "v2 registry",
credentials: []docker.RegistryCredentials{
{
Registry: "https://index.docker.io/v2/",
Username: "testuser",
Password: "testpassword",
},
},
wantErr: false,
},
{
name: "docker registry credentials",
credentials: []docker.RegistryCredentials{
{
Registry: "https://index.docker.io/v1/",
Username: "testuser",
Password: "testpassword",
},
{
Registry: "https://docker.io",
Username: "dockeruser",
Password: "dockerpassword",
},
},
wantErr: false,
},
{
name: "empty docker registry",
credentials: []docker.RegistryCredentials{
{
Registry: "https://index.docker.io/v1/",
Username: "testuser",
Password: "testpassword",
},
{
Registry: "https://docker.io",
Username: "dockeruser",
Password: "",
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := config.CreateDockerConfig(tt.credentials, tempDir)
if (err != nil) != tt.wantErr {
t.Errorf("CreateDockerConfig() error = %v, wantErr %v", err, tt.wantErr)
return
}
})
}
}
+33
View File
@@ -0,0 +1,33 @@
package main
import (
"strings"
)
// CustomStringSliceFlag is like a regular StringSlice flag but with
// semicolon as a delimiter
type CustomStringSliceFlag struct {
Value []string
}
func (f *CustomStringSliceFlag) GetValue() []string {
if f.Value == nil {
return make([]string, 0)
}
return f.Value
}
func (f *CustomStringSliceFlag) String() string {
if f.Value == nil {
return ""
}
return strings.Join(f.Value, ";")
}
func (f *CustomStringSliceFlag) Set(v string) error {
for _, s := range strings.Split(v, ";") {
s = strings.TrimSpace(s)
f.Value = append(f.Value, s)
}
return nil
}
+478 -57
View File
@@ -3,21 +3,23 @@ package main
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"strings"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ecr"
"github.com/aws/aws-sdk-go-v2/service/ecrpublic"
awsv1 "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/session"
ecrv1 "github.com/aws/aws-sdk-go/service/ecr"
ecrpublicv1 "github.com/aws/aws-sdk-go/service/ecrpublic"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/aws/smithy-go"
"github.com/hashicorp/go-version"
"github.com/joho/godotenv"
@@ -28,14 +30,17 @@ import (
kaniko "github.com/drone/drone-kaniko"
"github.com/drone/drone-kaniko/pkg/artifact"
"github.com/drone/drone-kaniko/pkg/docker"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/crane"
)
const (
accessKeyEnv string = "AWS_ACCESS_KEY_ID"
dockerConfigPath string = "/kaniko/.docker"
secretKeyEnv string = "AWS_SECRET_ACCESS_KEY"
dockerConfigPath string = "/kaniko/.docker/config.json"
ecrPublicDomain string = "public.ecr.aws"
kanikoVersionEnv string = "KANIKO_VERSION"
sessionKeyEnv string = "AWS_SESSION_TOKEN"
oneDotEightVersion string = "1.8.0"
defaultDigestFile string = "/kaniko/digest-file"
@@ -67,18 +72,18 @@ func main() {
},
cli.StringFlag{
Name: "docker-registry",
Usage: "docker registry",
EnvVar: "PLUGIN_DOCKER_REGISTRY,DOCKER_REGISTRY",
Usage: "Docker registry for base image",
EnvVar: "PLUGIN_DOCKER_REGISTRY,DOCKER_REGISTRY,PLUGIN_BASE_IMAGE_REGISTRY",
},
cli.StringFlag{
Name: "docker-username",
Usage: "docker username",
EnvVar: "PLUGIN_USERNAME,DOCKER_USERNAME",
Usage: "Docker username for base image registry",
EnvVar: "PLUGIN_USERNAME,PLUGIN_DOCKER_USERNAME,PLUGIN_BASE_IMAGE_USERNAME,DOCKER_USERNAME",
},
cli.StringFlag{
Name: "docker-password",
Usage: "docker password",
EnvVar: "PLUGIN_PASSWORD,DOCKER_PASSWORD",
Usage: "Docker password for base image registry",
EnvVar: "PLUGIN_PASSWORD,PLUGIN_DOCKER_PASSWORD,PLUGIN_BASE_IMAGE_PASSWORD,DOCKER_PASSWORD",
},
cli.StringFlag{
Name: "context",
@@ -123,6 +128,17 @@ func main() {
Usage: "build args",
EnvVar: "PLUGIN_BUILD_ARGS",
},
cli.GenericFlag{
Name: "args-new",
Usage: "build args new",
EnvVar: "PLUGIN_BUILD_ARGS_NEW",
Value: new(CustomStringSliceFlag),
},
cli.BoolFlag{
Name: "plugin-multiple-build-agrs",
Usage: "plugin multiple build agrs",
EnvVar: "PLUGIN_MULTIPLE_BUILD_ARGS",
},
cli.StringFlag{
Name: "target",
Usage: "build target",
@@ -234,6 +250,177 @@ func main() {
Usage: "build only used stages",
EnvVar: "PLUGIN_SKIP_UNUSED_STAGES",
},
cli.StringFlag{
Name: "cache-dir",
Usage: "Set this flag to specify a local directory cache for base images",
EnvVar: "PLUGIN_CACHE_DIR",
},
cli.BoolFlag{
Name: "cache-copy-layers",
Usage: "Enable or disable copying layers from the cache.",
EnvVar: "PLUGIN_CACHE_COPY_LAYERS",
},
cli.BoolFlag{
Name: "cache-run-layers",
Usage: "Enable or disable running layers from the cache.",
EnvVar: "PLUGIN_CACHE_RUN_LAYERS",
},
cli.BoolFlag{
Name: "cleanup",
Usage: "Enable or disable cleanup of temporary files.",
EnvVar: "PLUGIN_CLEANUP",
},
cli.BoolFlag{
Name: "compressed-caching",
Usage: "Enable or disable compressed caching.",
EnvVar: "PLUGIN_COMPRESSED_CACHING",
},
cli.StringFlag{
Name: "context-sub-path",
Usage: "Sub-path within the context to build.",
EnvVar: "PLUGIN_CONTEXT_SUB_PATH",
},
cli.StringFlag{
Name: "custom-platform",
Usage: "Platform to use for building.",
EnvVar: "PLUGIN_CUSTOM_PLATFORM",
},
cli.BoolFlag{
Name: "force",
Usage: "Force building the image even if it already exists.",
EnvVar: "PLUGIN_FORCE",
},
cli.StringFlag{
Name: "image-name-with-digest-file",
Usage: "Write image name with digest to a file.",
EnvVar: "PLUGIN_IMAGE_NAME_WITH_DIGEST_FILE",
},
cli.StringFlag{
Name: "image-name-tag-with-digest-file",
Usage: "Write image name with tag and digest to a file.",
EnvVar: "PLUGIN_IMAGE_NAME_TAG_WITH_DIGEST_FILE",
},
cli.BoolFlag{
Name: "insecure",
Usage: "Allow connecting to registries without TLS.",
EnvVar: "PLUGIN_INSECURE",
},
cli.BoolFlag{
Name: "insecure-pull",
Usage: "Allow insecure pulls from the registry.",
EnvVar: "PLUGIN_INSECURE_PULL",
},
cli.StringFlag{
Name: "insecure-registry",
Usage: "Use plain HTTP for registry communication.",
EnvVar: "PLUGIN_INSECURE_REGISTRY",
},
cli.StringFlag{
Name: "log-format",
Usage: "Set the log format for build output.",
EnvVar: "PLUGIN_LOG_FORMAT",
},
cli.BoolFlag{
Name: "log-timestamp",
Usage: "Show timestamps in build output.",
EnvVar: "PLUGIN_LOG_TIMESTAMP",
},
cli.StringFlag{
Name: "oci-layout-path",
Usage: "Directory to store OCI layout.",
EnvVar: "PLUGIN_OCI_LAYOUT_PATH",
},
cli.IntFlag{
Name: "push-retry",
Usage: "Number of times to retry pushing an image.",
EnvVar: "PLUGIN_PUSH_RETRY",
},
cli.StringFlag{
Name: "registry-certificate",
Usage: "Path to a file containing a registry certificate.",
EnvVar: "PLUGIN_REGISTRY_CERTIFICATE",
},
cli.StringFlag{
Name: "registry-client-cert",
Usage: "Path to a file containing a registry client certificate.",
EnvVar: "PLUGIN_REGISTRY_CLIENT_CERT",
},
cli.BoolFlag{
Name: "skip-default-registry-fallback",
Usage: "Skip Docker Hub and default registry fallback.",
EnvVar: "PLUGIN_SKIP_DEFAULT_REGISTRY_FALLBACK",
},
cli.BoolFlag{
Name: "reproducible",
Usage: "Create a reproducible image.",
EnvVar: "PLUGIN_REPRODUCIBLE",
},
cli.BoolFlag{
Name: "single-snapshot",
Usage: "Only create a single snapshot of the image.",
EnvVar: "PLUGIN_SINGLE_SNAPSHOT",
},
cli.BoolFlag{
Name: "skip-push-permission-check",
Usage: "Skip permission check when pushing.",
EnvVar: "PLUGIN_SKIP_PUSH_PERMISSION_CHECK",
},
cli.BoolFlag{
Name: "skip-tls-verify-pull",
Usage: "Skip TLS verification when pulling.",
EnvVar: "PLUGIN_SKIP_TLS_VERIFY_PULL",
},
cli.BoolFlag{
Name: "skip-tls-verify-registry",
Usage: "Skip TLS verification when connecting to a registry.",
EnvVar: "PLUGIN_SKIP_TLS_VERIFY_REGISTRY",
},
cli.BoolFlag{
Name: "use-new-run",
Usage: "Skip TLS verification when connecting to a registry.",
EnvVar: "PLUGIN_USE_NEW_RUN",
},
cli.BoolFlag{
Name: "ignore-var-run",
Usage: "Ignore the /var/run directory during build.",
EnvVar: "PLUGIN_IGNORE_VAR_RUN",
},
cli.StringFlag{
Name: "ignore-path",
Usage: "Path to ignore during the build.",
EnvVar: "PLUGIN_IGNORE_PATH",
},
cli.IntFlag{
Name: "image-fs-extract-retry",
Usage: "Number of retries for extracting filesystem layers.",
EnvVar: "PLUGIN_IMAGE_FS_EXTRACT_RETRY",
},
cli.IntFlag{
Name: "image-download-retry",
Usage: "Number of retries for downloading base images.",
EnvVar: "PLUGIN_IMAGE_DOWNLOAD_RETRY",
},
cli.StringFlag{
Name: "oidc-token-id",
Usage: "OIDC token for assuming role via web identity",
EnvVar: "PLUGIN_OIDC_TOKEN_ID",
},
cli.StringFlag{
Name: "tar-path",
Usage: "Set this flag to save the image as a tarball at path",
EnvVar: "PLUGIN_TAR_PATH, PLUGIN_DESTINATION_TAR_PATH",
},
cli.StringFlag{
Name: "source-tar-path",
Usage: "Set this flag for the source tarball during push operations.",
EnvVar: "PLUGIN_SOURCE_TAR_PATH",
},
cli.BoolFlag{
Name: "push-only",
Usage: "Specify if the operation is push-only",
EnvVar: "PLUGIN_PUSH_ONLY",
},
}
if err := app.Run(os.Args); err != nil {
@@ -246,10 +433,23 @@ func run(c *cli.Context) error {
registry := c.String("registry")
region := c.String("region")
noPush := c.Bool("no-push")
pushOnly := c.Bool("push-only")
assumeRole := c.String("assume-role")
externalId := c.String("external-id")
oidcToken := c.String("oidc-token-id")
dockerConfig, err := createDockerConfig(
// Validate flags
if noPush && pushOnly {
return fmt.Errorf("no-push and push-only flags cannot be used together")
}
// Handle push-only operation
if pushOnly {
return handlePushOnly(c)
}
// setup docker config for azure registry and base image docker registry
err := setDockerAuth(
c.String("docker-registry"),
c.String("docker-username"),
c.String("docker-password"),
@@ -260,18 +460,10 @@ func run(c *cli.Context) error {
externalId,
region,
noPush,
oidcToken,
)
if err != nil {
return err
}
jsonBytes, err := json.Marshal(dockerConfig)
if err != nil {
return err
}
if err := ioutil.WriteFile(dockerConfigPath, jsonBytes, 0644); err != nil {
return err
return errors.Wrap(err, "failed to create docker config")
}
// only create repository when pushing and create-repository is true
@@ -303,28 +495,64 @@ func run(c *cli.Context) error {
plugin := kaniko.Plugin{
Build: kaniko.Build{
DroneCommitRef: c.String("drone-commit-ref"),
DroneRepoBranch: c.String("drone-repo-branch"),
Dockerfile: c.String("dockerfile"),
Context: c.String("context"),
Tags: c.StringSlice("tags"),
AutoTag: c.Bool("auto-tag"),
AutoTagSuffix: c.String("auto-tag-suffix"),
ExpandTag: c.Bool("expand-tag"),
Args: c.StringSlice("args"),
Target: c.String("target"),
Repo: fmt.Sprintf("%s/%s", c.String("registry"), c.String("repo")),
Mirrors: c.StringSlice("registry-mirrors"),
Labels: c.StringSlice("custom-labels"),
SnapshotMode: c.String("snapshot-mode"),
EnableCache: c.Bool("enable-cache"),
CacheRepo: fmt.Sprintf("%s/%s", c.String("registry"), c.String("cache-repo")),
CacheTTL: c.Int("cache-ttl"),
DigestFile: defaultDigestFile,
NoPush: noPush,
Verbosity: c.String("verbosity"),
Platform: c.String("platform"),
SkipUnusedStages: c.Bool("skip-unused-stages"),
DroneCommitRef: c.String("drone-commit-ref"),
DroneRepoBranch: c.String("drone-repo-branch"),
Dockerfile: c.String("dockerfile"),
Context: c.String("context"),
Tags: c.StringSlice("tags"),
AutoTag: c.Bool("auto-tag"),
AutoTagSuffix: c.String("auto-tag-suffix"),
ExpandTag: c.Bool("expand-tag"),
Args: c.StringSlice("args"),
ArgsNew: c.Generic("args-new").(*CustomStringSliceFlag).GetValue(),
IsMultipleBuildArgs: c.Bool("plugin-multiple-build-agrs"),
Target: c.String("target"),
Repo: fmt.Sprintf("%s/%s", c.String("registry"), c.String("repo")),
Mirrors: c.StringSlice("registry-mirrors"),
Labels: c.StringSlice("custom-labels"),
SnapshotMode: c.String("snapshot-mode"),
EnableCache: c.Bool("enable-cache"),
CacheRepo: fmt.Sprintf("%s/%s", c.String("registry"), c.String("cache-repo")),
CacheTTL: c.Int("cache-ttl"),
DigestFile: defaultDigestFile,
NoPush: noPush,
Verbosity: c.String("verbosity"),
Platform: c.String("platform"),
SkipUnusedStages: c.Bool("skip-unused-stages"),
CacheDir: c.String("cache-dir"),
CacheCopyLayers: c.Bool("cache-copy-layers"),
CacheRunLayers: c.Bool("cache-run-layers"),
Cleanup: c.Bool("cleanup"),
ContextSubPath: c.String("context-sub-path"),
CustomPlatform: c.String("custom-platform"),
Force: c.Bool("force"),
ImageNameWithDigestFile: c.String("image-name-with-digest-file"),
ImageNameTagWithDigestFile: c.String("image-name-tag-with-digest-file"),
Insecure: c.Bool("insecure"),
InsecurePull: c.Bool("insecure-pull"),
InsecureRegistry: c.String("insecure-registry"),
Label: c.String("label"),
LogFormat: c.String("log-format"),
LogTimestamp: c.Bool("log-timestamp"),
OCILayoutPath: c.String("oci-layout-path"),
PushRetry: c.Int("push-retry"),
RegistryCertificate: c.String("registry-certificate"),
RegistryClientCert: c.String("registry-client-cert"),
SkipDefaultRegistryFallback: c.Bool("skip-default-registry-fallback"),
Reproducible: c.Bool("reproducible"),
SingleSnapshot: c.Bool("single-snapshot"),
SkipTLSVerify: c.Bool("skip-tls-verify"),
SkipPushPermissionCheck: c.Bool("skip-push-permission-check"),
SkipTLSVerifyPull: c.Bool("skip-tls-verify-pull"),
SkipTLSVerifyRegistry: c.Bool("skip-tls-verify-registry"),
UseNewRun: c.Bool("use-new-run"),
IgnorePath: c.String("ignore-path"),
IgnorePaths: c.StringSlice("ignore-paths"),
ImageFSExtractRetry: c.Int("image-fs-extract-retry"),
ImageDownloadRetry: c.Int("image-download-retry"),
TarPath: c.String("tar-path"),
SourceTarPath: c.String("source-tar-path"),
PushOnly: c.Bool("push-only"),
},
Artifact: kaniko.Artifact{
Tags: c.StringSlice("tags"),
@@ -334,44 +562,77 @@ func run(c *cli.Context) error {
RegistryType: artifact.ECR,
},
}
if c.IsSet("compressed-caching") {
flag := c.Bool("compressed-caching")
plugin.Build.CompressedCaching = &flag
}
if c.IsSet("ignore-var-run") {
flag := c.Bool("ignore-var-run")
plugin.Build.IgnoreVarRun = &flag
}
return plugin.Exec()
}
func createDockerConfig(dockerRegistry, dockerUsername, dockerPassword, accessKey, secretKey,
registry, assumeRole, externalId, region string, noPush bool) (*docker.Config, error) {
func setDockerAuth(dockerRegistry, dockerUsername, dockerPassword, accessKey, secretKey,
registry, assumeRole, externalId, region string, noPush bool, oidcToken string) error {
dockerConfig := docker.NewConfig()
if dockerUsername != "" {
// if no docker registry provided, use dockerhub by default
if len(dockerRegistry) == 0 {
dockerRegistry = docker.RegistryV1
credentials := []docker.RegistryCredentials{}
// set docker credentials for base image registry
if dockerRegistry != "" {
pullFromRegistryCreds := docker.RegistryCredentials{
Registry: dockerRegistry,
Username: dockerUsername,
Password: dockerPassword,
}
dockerConfig.SetAuth(dockerRegistry, dockerUsername, dockerPassword)
credentials = append(credentials, pullFromRegistryCreds)
}
if assumeRole != "" {
if assumeRole != "" && oidcToken != "" {
oidcAccessKey, oidcSecretKey, oidcSessionKey, err := getOidcCreds(oidcToken, assumeRole)
if err != nil {
return err
}
_ = os.Setenv(accessKeyEnv, oidcAccessKey)
_ = os.Setenv(secretKeyEnv, oidcSecretKey)
_ = os.Setenv(sessionKeyEnv, oidcSessionKey)
// kaniko-executor >=1.8.0 does not require additional cred helper logic for ECR,
// as it discovers ECR repositories automatically and acts accordingly.
if isKanikoVersionBelowOneDotEight(os.Getenv(kanikoVersionEnv)) {
dockerConfig.SetCredHelper(ecrPublicDomain, "ecr-login")
dockerConfig.SetCredHelper(registry, "ecr-login")
}
} else if assumeRole != "" {
var err error
username, password, registry, err := getAssumeRoleCreds(region, assumeRole, externalId, "")
if err != nil {
return nil, err
return err
}
dockerConfig.SetAuth(registry, username, password)
pushToRegistryCreds := docker.RegistryCredentials{
Registry: registry,
Username: username,
Password: password,
}
credentials = append(credentials, pushToRegistryCreds)
} else if !noPush || accessKey != "" {
// only setup auth when pushing or credentials are defined
if registry == "" {
return nil, fmt.Errorf("registry must be specified")
return fmt.Errorf("registry must be specified")
}
// If IAM role is used, access key & secret key are not required
if accessKey != "" && secretKey != "" {
err := os.Setenv(accessKeyEnv, accessKey)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("failed to set %s environment variable", accessKeyEnv))
return errors.Wrap(err, fmt.Sprintf("failed to set %s environment variable", accessKeyEnv))
}
err = os.Setenv(secretKeyEnv, secretKey)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("failed to set %s environment variable", secretKeyEnv))
return errors.Wrap(err, fmt.Sprintf("failed to set %s environment variable", secretKeyEnv))
}
}
@@ -382,8 +643,7 @@ func createDockerConfig(dockerRegistry, dockerUsername, dockerPassword, accessKe
dockerConfig.SetCredHelper(registry, "ecr-login")
}
}
return dockerConfig, nil
return dockerConfig.CreateDockerConfig(credentials, dockerConfigPath)
}
func createRepository(region, repo, registry, assumeRole, externalId string) error {
@@ -584,3 +844,164 @@ func isKanikoVersionBelowOneDotEight(v string) bool {
return currVer.LessThan(oneEightVer)
}
func getOidcCreds(oidcToken, assumeRole string) (string, string, string, error) {
// Create a new session
sess, err := session.NewSession()
if err != nil {
return "", "", "", fmt.Errorf("failed to create AWS session: %w", err)
}
// Create a new STS client
svc := sts.New(sess)
// Prepare the input parameters for the STS call
duration := int64(time.Hour / time.Second)
input := &sts.AssumeRoleWithWebIdentityInput{
RoleArn: aws.String(assumeRole),
RoleSessionName: aws.String("kaniko-ecr-oidc"),
WebIdentityToken: aws.String(oidcToken),
DurationSeconds: aws.Int64(duration),
}
// Call the AssumeRoleWithWebIdentity function
result, err := svc.AssumeRoleWithWebIdentity(input)
if err != nil {
return "", "", "", fmt.Errorf("failed to assume role with web identity: %w", err)
}
// Check if credentials exist in the result
if result.Credentials == nil {
return "", "", "", errors.New("no credentials returned by AssumeRoleWithWebIdentity")
}
// Return the credentials
return *result.Credentials.AccessKeyId, *result.Credentials.SecretAccessKey, *result.Credentials.SessionToken, nil
}
func createECRSession(region, accessKey, secretKey, sessionToken string) *ecrv1.ECR {
sess := session.Must(session.NewSession(&awsv1.Config{
Region: awsv1.String(region),
Credentials: credentials.NewStaticCredentials(
accessKey,
secretKey,
sessionToken,
),
}))
return ecrv1.New(sess)
}
func getECRCredentials(region, registry, assumeRole, externalId, accessKey, secretKey, oidcToken string) (string, string, error) {
if assumeRole != "" && oidcToken != "" {
// For OIDC auth with assume role
awsAccessKey, awsSecretKey, awsSessionToken, err := getOidcCreds(oidcToken, assumeRole)
if err != nil {
return "", "", fmt.Errorf("failed to get OIDC credentials: %w", err)
}
// Create ECR session and get auth info
svc := createECRSession(region, awsAccessKey, awsSecretKey, awsSessionToken)
username, password, _, err := getAuthInfo(svc)
if err != nil {
return "", "", fmt.Errorf("failed to get ECR credentials: %w", err)
}
return username, password, nil
} else if assumeRole != "" {
// For assume role auth
username, password, _, err := getAssumeRoleCreds(region, assumeRole, externalId, "")
if err != nil {
return "", "", fmt.Errorf("failed to get ECR credentials: %w", err)
}
return username, password, nil
} else if accessKey != "" && secretKey != "" {
// For direct credentials
sess := session.Must(session.NewSession(&awsv1.Config{
Region: awsv1.String(region),
Credentials: credentials.NewStaticCredentials(
accessKey,
secretKey,
"",
),
}))
svc := ecrv1.New(sess)
username, password, _, err := getAuthInfo(svc)
if err != nil {
return "", "", fmt.Errorf("failed to get ECR credentials: %w", err)
}
return username, password, nil
} else {
// For IAM role auth (default credentials)
sess := session.Must(session.NewSession(&awsv1.Config{
Region: awsv1.String(region),
}))
svc := ecrv1.New(sess)
username, password, _, err := getAuthInfo(svc)
if err != nil {
return "", "", fmt.Errorf("failed to get ECR credentials: %w", err)
}
return username, password, nil
}
}
func handlePushOnly(c *cli.Context) error {
sourceTarPath := c.String("source-tar-path")
if sourceTarPath == "" {
return fmt.Errorf("source_tar_path is required when push_only is set")
}
if _, err := os.Stat(sourceTarPath); os.IsNotExist(err) {
return fmt.Errorf("image tarball does not exist at path: %s", sourceTarPath)
}
repo := c.String("repo")
registry := c.String("registry")
if repo == "" || registry == "" {
return fmt.Errorf("repository and registry must be specified for push-only operation")
}
// Load the image from the tarball
img, err := crane.Load(sourceTarPath)
if err != nil {
return fmt.Errorf("failed to load image from tarball: %v", err)
}
// Get ECR credentials using the common function
username, password, err := getECRCredentials(
c.String("region"),
registry,
c.String("assume-role"),
c.String("external-id"),
c.String("access-key"),
c.String("secret-key"),
c.String("oidc-token-id"),
)
if err != nil {
return err
}
// Setup crane auth
opts := []crane.Option{
crane.WithAuth(&authn.Basic{
Username: username,
Password: password,
}),
}
// Push for each tag
tags := c.StringSlice("tags")
if len(tags) == 0 {
tags = []string{"latest"}
}
for _, tag := range tags {
dest := fmt.Sprintf("%s/%s:%s", registry, repo, tag)
if err := crane.Push(img, dest, opts...); err != nil {
return fmt.Errorf("failed to push image to %s: %v", dest, err)
}
fmt.Printf("Successfully pushed image to %s\n", dest)
}
return nil
}
+32 -116
View File
@@ -1,129 +1,45 @@
package main
import (
"encoding/base64"
"io/ioutil"
"os"
"reflect"
"testing"
"github.com/drone/drone-kaniko/pkg/docker"
"github.com/stretchr/testify/assert"
)
func TestCreateDockerConfig(t *testing.T) {
got, err := createDockerConfig(
"",
"docker-username",
"docker-password",
"access-key",
"secret-key",
"ecr-registry",
"",
"",
"",
false,
)
if err != nil {
t.Error("failed to create docker config")
func TestCreateDockerConfigForECRWithBaseRegistry(t *testing.T) {
accessKey := "access-key"
secretKey := "secret-key"
ecrRegistry := "ecr-registry"
dockerUsername := "dockeruser"
dockerPassword := "dockerpass"
dockerRegistry := "https://index.docker.io/v1/"
tempDir, err := ioutil.TempDir("", "docker-config-test")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)
config := docker.NewConfig()
pullFromRegistryCreds := docker.RegistryCredentials{
Registry: dockerRegistry,
Username: dockerUsername,
Password: dockerPassword,
}
credentials := []docker.RegistryCredentials{
{Registry: ecrRegistry, Username: accessKey, Password: secretKey},
pullFromRegistryCreds,
}
want := docker.NewConfig()
want.SetAuth(docker.RegistryV1, "docker-username", "docker-password")
want.SetCredHelper(docker.RegistryECRPublic, "ecr-login")
want.SetCredHelper("ecr-registry", "ecr-login")
err = config.CreateDockerConfig(credentials, tempDir)
assert.NoError(t, err)
if !reflect.DeepEqual(want, got) {
t.Errorf("not equal:\n want: %#v\n got: %#v", want, got)
}
}
expectedECRAuth := docker.Auth{Auth: base64.StdEncoding.EncodeToString([]byte(accessKey + ":" + secretKey))}
assert.Equal(t, expectedECRAuth, config.Auths[ecrRegistry])
func TestCreateDockerConfigFromGivenRegistry(t *testing.T) {
got, err := createDockerConfig(
"docker-registry",
"docker-username",
"docker-password",
"access-key",
"secret-key",
"ecr-registry",
"",
"",
"",
false,
)
if err != nil {
t.Error("failed to create docker config")
}
want := docker.NewConfig()
want.SetAuth("docker-registry", "docker-username", "docker-password")
want.SetCredHelper(docker.RegistryECRPublic, "ecr-login")
want.SetCredHelper("ecr-registry", "ecr-login")
if !reflect.DeepEqual(want, got) {
t.Errorf("not equal:\n want: %#v\n got: %#v", want, got)
}
}
func TestCreateDockerConfigKanikoOneDotEight(t *testing.T) {
os.Setenv(kanikoVersionEnv, "1.8.1")
defer os.Setenv(kanikoVersionEnv, "")
got, err := createDockerConfig(
"",
"docker-username",
"docker-password",
"access-key",
"secret-key",
"ecr-registry",
"",
"",
"",
false,
)
if err != nil {
t.Error("failed to create docker config")
}
want := docker.NewConfig()
want.SetAuth(docker.RegistryV1, "docker-username", "docker-password")
if !reflect.DeepEqual(want, got) {
t.Errorf("not equal:\n want: %#v\n got: %#v", want, got)
}
}
func TestVersionComparison(t *testing.T) {
tests := []struct {
title string
version string
expected bool
}{
{
title: "Kaniko 1.6.0 version",
version: "1.6.0",
expected: true,
},
{
title: "Kaniko 1.8.0 version",
version: "1.8.0",
expected: false,
},
{
title: "Kaniko 1.8.1 version",
version: "1.8.1",
expected: false,
},
{
title: "Empty kaniko version",
version: "",
expected: true,
},
{
title: "Kaniko version 1.10.0",
version: "1.10.0",
expected: false,
},
}
for _, test := range tests {
got := isKanikoVersionBelowOneDotEight(test.version)
if got != test.expected {
t.Fatalf("test name: %s, expected: %v, got: %v", test.title, test.expected, got)
}
}
}
expectedDockerAuth := docker.Auth{Auth: base64.StdEncoding.EncodeToString([]byte(dockerUsername + ":" + dockerPassword))}
assert.Equal(t, expectedDockerAuth, config.Auths[dockerRegistry])
}
+601
View File
@@ -0,0 +1,601 @@
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/joho/godotenv"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
kaniko "github.com/drone/drone-kaniko"
"github.com/drone/drone-kaniko/pkg/artifact"
"github.com/drone/drone-kaniko/pkg/docker"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/crane"
)
const (
dockerConfigPath string = "/kaniko/.docker"
// GAR JSON key file path
garKeyPath string = "/kaniko/config.json"
garEnvVariable string = "GOOGLE_APPLICATION_CREDENTIALS"
defaultDigestFile string = "/kaniko/digest-file"
)
var (
version = "unknown"
)
func main() {
// Load env-file if it exists first
if env := os.Getenv("PLUGIN_ENV_FILE"); env != "" {
if err := godotenv.Load(env); err != nil {
logrus.Fatal(err)
}
}
app := cli.NewApp()
app.Name = "kaniko gar plugin"
app.Usage = "kaniko gar plugin"
app.Action = run
app.Version = version
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "dockerfile",
Usage: "build dockerfile",
Value: "Dockerfile",
EnvVar: "PLUGIN_DOCKERFILE",
},
cli.StringFlag{
Name: "context",
Usage: "build context",
Value: ".",
EnvVar: "PLUGIN_CONTEXT",
},
cli.StringFlag{
Name: "drone-commit-ref",
Usage: "git commit ref passed by Drone",
EnvVar: "DRONE_COMMIT_REF",
},
cli.StringFlag{
Name: "drone-repo-branch",
Usage: "git repository default branch passed by Drone",
EnvVar: "DRONE_REPO_BRANCH",
},
cli.StringSliceFlag{
Name: "tags",
Usage: "build tags",
Value: &cli.StringSlice{"latest"},
EnvVar: "PLUGIN_TAGS",
FilePath: ".tags",
},
cli.BoolFlag{
Name: "expand-tag",
Usage: "enable for semver tagging",
EnvVar: "PLUGIN_EXPAND_TAG",
},
cli.BoolFlag{
Name: "auto-tag",
Usage: "enable auto generation of build tags",
EnvVar: "PLUGIN_AUTO_TAG",
},
cli.StringFlag{
Name: "auto-tag-suffix",
Usage: "the suffix of auto build tags",
EnvVar: "PLUGIN_AUTO_TAG_SUFFIX",
},
cli.StringSliceFlag{
Name: "args",
Usage: "build args",
EnvVar: "PLUGIN_BUILD_ARGS",
},
cli.StringFlag{
Name: "target",
Usage: "build target",
EnvVar: "PLUGIN_TARGET",
},
cli.StringFlag{
Name: "repo",
Usage: "gar repository",
EnvVar: "PLUGIN_REPO",
},
cli.StringSliceFlag{
Name: "custom-labels",
Usage: "additional k=v labels",
EnvVar: "PLUGIN_CUSTOM_LABELS",
},
cli.StringFlag{
Name: "registry",
Usage: "gar registry",
EnvVar: "PLUGIN_REGISTRY",
},
cli.StringFlag{
Name: "base-image-username",
Usage: "Docker username for base image registry",
EnvVar: "PLUGIN_DOCKER_USERNAME,PLUGIN_BASE_IMAGE_USERNAME,DOCKER_USERNAME",
},
cli.StringFlag{
Name: "base-image-password",
Usage: "Docker password for base image registry",
EnvVar: "PLUGIN_DOCKER_PASSWORD,PLUGIN_BASE_IMAGE_PASSWORD,DOCKER_PASSWORD",
},
cli.StringFlag{
Name: "base-image-registry",
Usage: "Docker registry for base image registry",
EnvVar: "PLUGIN_DOCKER_REGISTRY,PLUGIN_BASE_IMAGE_REGISTRY,DOCKER_REGISTRY",
},
cli.StringSliceFlag{
Name: "registry-mirrors",
Usage: "docker registry mirrors",
EnvVar: "PLUGIN_REGISTRY_MIRRORS",
},
cli.StringFlag{
Name: "json-key",
Usage: "docker username",
EnvVar: "PLUGIN_JSON_KEY",
},
cli.StringFlag{
Name: "snapshot-mode",
Usage: "Specify one of full, redo or time as snapshot mode",
EnvVar: "PLUGIN_SNAPSHOT_MODE",
},
cli.BoolFlag{
Name: "enable-cache",
Usage: "Set this flag to opt into caching with kaniko",
EnvVar: "PLUGIN_ENABLE_CACHE",
},
cli.StringFlag{
Name: "cache-repo",
Usage: "Remote repository that will be used to store cached layers. Cache repo should be present in specified registry. enable-cache needs to be set to use this flag",
EnvVar: "PLUGIN_CACHE_REPO",
},
cli.IntFlag{
Name: "cache-ttl",
Usage: "Cache timeout in hours. Defaults to two weeks.",
EnvVar: "PLUGIN_CACHE_TTL",
},
cli.StringFlag{
Name: "artifact-file",
Usage: "Artifact file location that will be generated by the plugin. This file will include information of docker images that are uploaded by the plugin.",
EnvVar: "PLUGIN_ARTIFACT_FILE",
},
cli.BoolFlag{
Name: "no-push",
Usage: "Set this flag if you only want to build the image, without pushing to a registry",
EnvVar: "PLUGIN_NO_PUSH",
},
cli.BoolFlag{
Name: "push-only",
Usage: "Set this flag if you only want to push a pre-built image from a tarball",
EnvVar: "PLUGIN_PUSH_ONLY",
},
cli.StringFlag{
Name: "source-tar-path",
Usage: "Path to the local tarball to be pushed when push-only is set",
EnvVar: "PLUGIN_SOURCE_TAR_PATH",
},
cli.StringFlag{
Name: "tar-path",
Usage: "Set this flag to save the image as a tarball at path",
EnvVar: "PLUGIN_TAR_PATH,PLUGIN_DESTINATION_TAR_PATH",
},
cli.StringFlag{
Name: "verbosity",
Usage: "Set this flag as --verbosity=<panic|fatal|error|warn|info|debug|trace> to set the logging level for kaniko. Defaults to info.",
EnvVar: "PLUGIN_VERBOSITY",
},
cli.StringFlag{
Name: "platform",
Usage: "Allows to build with another default platform than the host, similarly to docker build --platform",
EnvVar: "PLUGIN_PLATFORM",
},
cli.BoolFlag{
Name: "skip-unused-stages",
Usage: "build only used stages",
EnvVar: "PLUGIN_SKIP_UNUSED_STAGES",
},
cli.StringFlag{
Name: "cache-dir",
Usage: "Set this flag to specify a local directory cache for base images",
EnvVar: "PLUGIN_CACHE_DIR",
},
cli.BoolFlag{
Name: "cache-copy-layers",
Usage: "Enable or disable copying layers from the cache.",
EnvVar: "PLUGIN_CACHE_COPY_LAYERS",
},
cli.BoolFlag{
Name: "cache-run-layers",
Usage: "Enable or disable running layers from the cache.",
EnvVar: "PLUGIN_CACHE_RUN_LAYERS",
},
cli.BoolFlag{
Name: "cleanup",
Usage: "Enable or disable cleanup of temporary files.",
EnvVar: "PLUGIN_CLEANUP",
},
cli.BoolFlag{
Name: "compressed-caching",
Usage: "Enable or disable compressed caching.",
EnvVar: "PLUGIN_COMPRESSED_CACHING",
},
cli.StringFlag{
Name: "context-sub-path",
Usage: "Sub-path within the context to build.",
EnvVar: "PLUGIN_CONTEXT_SUB_PATH",
},
cli.StringFlag{
Name: "custom-platform",
Usage: "Platform to use for building.",
EnvVar: "PLUGIN_CUSTOM_PLATFORM",
},
cli.BoolFlag{
Name: "force",
Usage: "Force building the image even if it already exists.",
EnvVar: "PLUGIN_FORCE",
},
cli.StringFlag{
Name: "image-name-with-digest-file",
Usage: "Write image name with digest to a file.",
EnvVar: "PLUGIN_IMAGE_NAME_WITH_DIGEST_FILE",
},
cli.StringFlag{
Name: "image-name-tag-with-digest-file",
Usage: "Write image name with tag and digest to a file.",
EnvVar: "PLUGIN_IMAGE_NAME_TAG_WITH_DIGEST_FILE",
},
cli.BoolFlag{
Name: "insecure",
Usage: "Allow connecting to registries without TLS.",
EnvVar: "PLUGIN_INSECURE",
},
cli.BoolFlag{
Name: "insecure-pull",
Usage: "Allow insecure pulls from the registry.",
EnvVar: "PLUGIN_INSECURE_PULL",
},
cli.StringFlag{
Name: "insecure-registry",
Usage: "Use plain HTTP for registry communication.",
EnvVar: "PLUGIN_INSECURE_REGISTRY",
},
cli.StringFlag{
Name: "log-format",
Usage: "Set the log format for build output.",
EnvVar: "PLUGIN_LOG_FORMAT",
},
cli.BoolFlag{
Name: "log-timestamp",
Usage: "Show timestamps in build output.",
EnvVar: "PLUGIN_LOG_TIMESTAMP",
},
cli.StringFlag{
Name