diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 7f2a721..2806df1 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -13,6 +13,7 @@ env: jobs: test: runs-on: ubuntu-latest + environment: test steps: - uses: actions/checkout@v2 @@ -23,6 +24,19 @@ jobs: - name: Test run: go test -v ./... + env: + TEST_DOCKER_PRIVATE_HOST: ${{ secrets.TEST_DOCKER_PRIVATE_HOST }} + TEST_DOCKER_PRIVATE_IMAGE: ${{ secrets.TEST_DOCKER_PRIVATE_IMAGE }} + TEST_DOCKER_PRIVATE_TAG: ${{ secrets.TEST_DOCKER_PRIVATE_TAG }} + TEST_DOCKER_PRIVATE_USERNAME: ${{ secrets.TEST_DOCKER_PRIVATE_USERNAME }} + TEST_DOCKER_PRIVATE_PASSWORD: ${{ secrets.TEST_DOCKER_PRIVATE_PASSWORD }} + + TEST_DOCKER_ECR_HOST: ${{ secrets.TEST_DOCKER_ECR_HOST }} + TEST_DOCKER_ECR_IMAGE: ${{ secrets.TEST_DOCKER_ECR_IMAGE }} + TEST_DOCKER_ECR_TAG: ${{ secrets.TEST_DOCKER_ECR_TAG }} + + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} docker-build-push: needs: test diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index b56312c..7bb8627 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -23,3 +23,7 @@ jobs: - name: Test run: go test -v ./... + env: + TEST_DOCKER_PRIVATE_SKIP: "1" + TEST_DOCKER_ECR_SKIP: "1" + \ No newline at end of file diff --git a/go.mod b/go.mod index e2dea02..8c6f4c0 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,12 @@ module github.com/pubg/kube-image-deployer go 1.16 require ( + github.com/aws/aws-sdk-go v1.40.49 github.com/google/go-containerregistry v0.6.0 - k8s.io/api v0.22.1 - k8s.io/apimachinery v0.22.1 - k8s.io/client-go v0.22.1 + github.com/joho/godotenv v1.4.0 + k8s.io/api v0.22.2 + k8s.io/apimachinery v0.22.2 + k8s.io/client-go v0.22.2 k8s.io/klog v1.0.0 k8s.io/klog/v2 v2.9.0 ) diff --git a/go.sum b/go.sum index 9fb11ac..7aeaeeb 100644 --- a/go.sum +++ b/go.sum @@ -91,6 +91,8 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/aws/aws-sdk-go v1.40.49 h1:kIbJYc4FZA2r4yxNU5giIR4HHLRkG9roFReWAsk0ZVQ= +github.com/aws/aws-sdk-go v1.40.49/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -181,6 +183,7 @@ github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJ github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/stargz-snapshotter/estargz v0.7.0 h1:1d/rydzTywc76lnjJb6qbPCiTiCwts49AzKps/Ecblw= github.com/containerd/stargz-snapshotter/estargz v0.7.0/go.mod h1:83VWDqHnurTKliEB0YvWMiCfLDwv4Cjj1X9Vk98GJZw= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= @@ -346,6 +349,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -360,7 +364,6 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -388,8 +391,8 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -436,7 +439,6 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= @@ -446,6 +448,12 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -463,6 +471,7 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.13.0 h1:2T7tUoQrQT+fQWdaY5rjWztFGAFwbGD04iPJg90ZiOs= github.com/klauspost/compress v1.13.0/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -821,15 +830,14 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 h1:ADo5wSpq2gqaCGQWzk7S5vd//0iyyLeAratkEoG5dLE= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= @@ -1044,7 +1052,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= @@ -1171,8 +1178,10 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1184,21 +1193,21 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.22.1 h1:ISu3tD/jRhYfSW8jI/Q1e+lRxkR7w9UwQEZ7FgslrwY= -k8s.io/api v0.22.1/go.mod h1:bh13rkTp3F1XEaLGykbyRD2QaTTzPm0e/BMd8ptFONY= +k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw= +k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.22.1 h1:DTARnyzmdHMz7bFWFDDm22AM4pLWTQECMpRTFu2d2OM= -k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= +k8s.io/apimachinery v0.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk= +k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.22.1 h1:jW0ZSHi8wW260FvcXHkIa0NLxFBQszTlhiAVsU5mopw= -k8s.io/client-go v0.22.1/go.mod h1:BquC5A4UOo4qVDUtoc04/+Nxp1MeHcVc1HJm1KmG8kk= +k8s.io/client-go v0.22.2 h1:DaSQgs02aCC1QcwUdkKZWOeaVsQjYvWv8ZazcZ6JcHc= +k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= @@ -1217,8 +1226,8 @@ k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAG k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9 h1:imL9YgXQ9p7xmPzHFm/vVd/cF78jad+n4wK1ABwYtMM= -k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a h1:8dYfu/Fc9Gz2rNJKB9IQRGgQOh2clmRzNIPPY1xLY5g= +k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/readme.md b/readme.md index 5fbf845..273760b 100644 --- a/readme.md +++ b/readme.md @@ -28,7 +28,8 @@ controllerWatchNamespace = *flag.String("controller-watch-namespace", "", "contr # 동작 방식 * kube-image-deployer label을 가진 Workload를 감시 대상으로 등록 합니다. * Workload의 annotation을 읽어 감시할 Image와 Container를 매핑합니다. -* 1분 간격(imageStringCacheTTLSec)으로 Docker Hub / Harbor 에서 Image:Tag의 Hash를 획득해 해당 Image:Tag를 사용하는 감시 대상 Workload의 Container에 Strategic Merge Patch를 진행합니다. +* 1분 간격(imageStringCacheTTLSec)으로 Docker Registry API v2로 이미지 정보와 이미지의 Digest Hash를 획득해 해당 사용중인 Workload의 Container에 Strategic Merge Patch를 진행합니다. +* Image Digest Hash로 패치하기 때문에 새 태그만 추가되고 이미지 Hash가 변경되지 않은 경우는 Workload가 재배포 되지 않습니다. (의도됨) # Kubernetes Yaml Examples ## Yaml 필수 구성 요소 @@ -42,7 +43,7 @@ controllerWatchNamespace = *flag.String("controller-watch-namespace", "", "contr * busybox:1.34.0 -> busybox@sha256:15f840677a5e245d9ea199eb9b026b1539208a5183621dced7b469f6aa678115 * Asterisk match tag - * busybox:1.34.* -> 1.34.0, 1.34.1, 1.34.2, ... + * busybox:1.34.* -> 1.34.0, 1.34.1, 1.34.2, ... -> busybox@sha256:15f840677a5e245d9ea199eb9b026b1539208a5183621dced7b469f6aa678115 ## Yaml Samples ### Deployments @@ -73,7 +74,7 @@ spec: image: busybox # no change args: ['sleep', '1000000'] - name: busybox2 - image: busybox # change to busybox:1.34.0 + image: busybox # change to busybox@sha:b862520da7361ea093806d292ce355188ae83f21e8e3b2a3ce4dbdba0a230f83 args: ['sleep', '1000000'] initContainers: - name: busybox-init @@ -81,9 +82,21 @@ spec: ``` # Private Repositories -Local의 Docker Creds로 Private Repository에 접근할 수 있습니다. Docker CLI가 있는 경우 Docker Login을 하면 되고, +kube-image-deployer는 Docker Creds로 기본 접근 권한을 획득합니다. + +## DockerHub / Harbor의 Private Registry 이미지 감시하기 +1. Kubernetes에 Private Registry 접근용 dockerconfig Secret을 생성합니다. +1. Auths에 URL과 접근 방법(username/password...) 입력합니다. +1. ```/root/.docker/config.json``` 위치에 Secret Volume을 마운트합니다. +1. kube-image-deployer는 AuthKeyChain을 통해 Creds에 마운트된 정보로 Private Registry를 접근합니다. + +## ECR의 Private Registry 이미지 감시하기 +두 가지 방법이 있습니다. +1. kube-image-deployer Service Account에 ECR 접근 권한을 가진 Role을 설정해 권한을 주는 방법 (AWS IRSA) +1. kube-image-deployer env에 ECR 접근 가능한 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY를 입력하는 방법 (AWS AccessToken) -없다면 ```$HOME/.docker/config.json```의 Auths를 통해 권한을 얻어 접근할 수 있습니다. +ECR 이미지 URL이 감지대상인 경우 kube-image-deloyer는 ECR의 GetAuthorizationToken을 호출해 Docker Auth Token을 획득하고 이 토큰을 사용해 Docker Registry API v2로 이미지 정보를 획득합니다. # Todo -* Test Code +* Add Test Code +* Support ECR Private Registry diff --git a/remoteRegistry/docker/docker.go b/remoteRegistry/docker/docker.go index 84ce883..eb2c4e2 100644 --- a/remoteRegistry/docker/docker.go +++ b/remoteRegistry/docker/docker.go @@ -12,7 +12,7 @@ import ( ) type RemoteRegistryDocker struct { - imageAuthMap map[string]authn.Keychain + imageAuthMap map[string]authn.Authenticator defaultPlatform *v1.Platform cache *util.Cache } @@ -20,7 +20,7 @@ type RemoteRegistryDocker struct { // NewRemoteRegistry returns a new RemoteRegistryDocker func NewRemoteRegistry() *RemoteRegistryDocker { d := &RemoteRegistryDocker{ - imageAuthMap: make(map[string]authn.Keychain), + imageAuthMap: make(map[string]authn.Authenticator), cache: util.NewCache(60), defaultPlatform: &v1.Platform{OS: "linux", Architecture: "amd64"}, } @@ -28,7 +28,7 @@ func NewRemoteRegistry() *RemoteRegistryDocker { return d } -func (d *RemoteRegistryDocker) WithImageAuthMap(imageAuthMap map[string]authn.Keychain) *RemoteRegistryDocker { +func (d *RemoteRegistryDocker) WithImageAuthMap(imageAuthMap map[string]authn.Authenticator) *RemoteRegistryDocker { d.imageAuthMap = imageAuthMap return d } @@ -60,7 +60,7 @@ func (d *RemoteRegistryDocker) GetImageString(url, tag, platformString string) ( } } -func (d *RemoteRegistryDocker) getAuthKeyChain(url string) authn.Keychain { +func (d *RemoteRegistryDocker) getAuthenticator(url string) authn.Authenticator { for key, value := range d.imageAuthMap { if strings.HasPrefix(url, key) { @@ -68,8 +68,24 @@ func (d *RemoteRegistryDocker) getAuthKeyChain(url string) authn.Keychain { } } - return authn.DefaultKeychain + if isECR(url) { // image가 ecr private repository 인 경우 + return NewECRAuthenticator(url) + } + + return nil + +} + +func (d *RemoteRegistryDocker) getRemoteOptions(url string) []remote.Option { + var options []remote.Option = []remote.Option{} + + if auth := d.getAuthenticator(url); auth != nil { + options = append(options, remote.WithAuth(auth)) + } else { + options = append(options, remote.WithAuthFromKeychain(authn.DefaultKeychain)) + } + return options } func (d *RemoteRegistryDocker) getImageDigestHash(url, tag, platformString string) (string, error) { @@ -81,7 +97,8 @@ func (d *RemoteRegistryDocker) getImageDigestHash(url, tag, platformString strin } fullUrl := fmt.Sprintf("%s:%s", url, tag) - authKeyChain := d.getAuthKeyChain(url) + options := d.getRemoteOptions(url) + options = append(options, remote.WithPlatform(*platform)) ref, err := name.ParseReference(fullUrl) if err != nil { @@ -89,7 +106,7 @@ func (d *RemoteRegistryDocker) getImageDigestHash(url, tag, platformString strin } hash, err := d.cache.Get(fullUrl, func() (interface{}, error) { - if img, err := remote.Image(ref, remote.WithAuthFromKeychain(authKeyChain), remote.WithPlatform(*platform)); err == nil { + if img, err := remote.Image(ref, options...); err == nil { if digest, err := img.Digest(); err == nil { return digest.String(), nil } else { @@ -105,7 +122,7 @@ func (d *RemoteRegistryDocker) getImageDigestHash(url, tag, platformString strin } func (d *RemoteRegistryDocker) getImageHighestVersionTag(url, tag, platformString string) (string, error) { - authKeyChain := d.getAuthKeyChain(url) + options := d.getRemoteOptions(url) repo, err := name.NewRepository(url) if nil != err { return "", err @@ -113,7 +130,7 @@ func (d *RemoteRegistryDocker) getImageHighestVersionTag(url, tag, platformStrin cacheKey := url + "___" + tag image, err := d.cache.Get(cacheKey, func() (interface{}, error) { - tags, err := remote.List(repo, remote.WithAuthFromKeychain(authKeyChain)) + tags, err := remote.List(repo, options...) if nil != err { return "", err } diff --git a/remoteRegistry/docker/docker_test.go b/remoteRegistry/docker/docker_test.go index 7d3ecc4..04343a8 100644 --- a/remoteRegistry/docker/docker_test.go +++ b/remoteRegistry/docker/docker_test.go @@ -1,18 +1,99 @@ package docker import ( + "fmt" + "os" "strings" "testing" + + "github.com/google/go-containerregistry/pkg/authn" + "github.com/joho/godotenv" ) +type testenv struct { + host string + image string + tag string + auth string + username string + password string +} + +var privateenv = testenv{} +var ecrenv = testenv{} + +func init() { + if err := godotenv.Load(); err != nil { + fmt.Printf("error loading .env file - %s", err) + } + + privateenv.host = os.Getenv("TEST_DOCKER_PRIVATE_HOST") + privateenv.image = os.Getenv("TEST_DOCKER_PRIVATE_IMAGE") + privateenv.tag = os.Getenv("TEST_DOCKER_PRIVATE_TAG") + privateenv.auth = os.Getenv("TEST_DOCKER_PRIVATE_AUTH") + privateenv.username = os.Getenv("TEST_DOCKER_PRIVATE_USERNAME") + privateenv.password = os.Getenv("TEST_DOCKER_PRIVATE_PASSWORD") + + ecrenv.host = os.Getenv("TEST_DOCKER_ECR_HOST") + ecrenv.image = os.Getenv("TEST_DOCKER_ECR_IMAGE") + ecrenv.tag = os.Getenv("TEST_DOCKER_ECR_TAG") +} + func TestGetImageStringAsterisk(t *testing.T) { r := NewRemoteRegistry() if s, err := r.GetImageString("busybox", "1.34.*", "linux/amd64"); err != nil { - t.Fatalf("TestGetImageStringAsterisk asterisk err: %v", err) - } else if !strings.Contains(s, "@sha256:") { - t.Fatalf("TestGetImageStringAsterisk asterisk no digest: %s", s) + t.Fatalf("err: %v", err) + } else if !strings.HasPrefix(s, "busybox@sha256:") { + t.Fatalf("no digest: %s", s) + } else { + t.Logf("success: %s", s) + } +} + +func TestGetImageFromPrivateRegistry(t *testing.T) { + if os.Getenv("TEST_DOCKER_PRIVATE_SKIP") != "" { + t.Log("skipping test") + return + } + + r := NewRemoteRegistry() + + if privateenv.auth != "" { + r.WithImageAuthMap(map[string]authn.Authenticator{ + privateenv.host: NewPrivateAuthenticatorWithAuth(privateenv.host, privateenv.auth), + }) + } else if privateenv.username != "" && privateenv.password != "" { + r.WithImageAuthMap(map[string]authn.Authenticator{ + privateenv.host: NewPrivateAuthenticator(privateenv.host, privateenv.username, privateenv.password), + }) + } else { + t.Fatalf("env not set") + } + + if s, err := r.GetImageString(privateenv.host+"/"+privateenv.image, privateenv.tag, "linux/amd64"); err != nil { + t.Fatalf("err: %v", err) + } else { + t.Logf("success: %s", s) + } +} + +func TestGetImageFromECR(t *testing.T) { + + if os.Getenv("TEST_DOCKER_ECR_SKIP") != "" { + t.Log("skipping test") + return + } + + r := NewRemoteRegistry() + + if ecrenv.host == "" || ecrenv.image == "" || ecrenv.tag == "" { + t.Fatalf("env not set") + } + + if s, err := r.GetImageString(ecrenv.host+"/"+ecrenv.image, ecrenv.tag, "linux/amd64"); err != nil { + t.Fatalf("err: %v, %+v", err, ecrenv) } else { - t.Logf("TestGetImageStringAsterisk asterisk success: %s", s) + t.Logf("success: %s", s) } } diff --git a/remoteRegistry/docker/ecr.go b/remoteRegistry/docker/ecr.go new file mode 100644 index 0000000..7188a81 --- /dev/null +++ b/remoteRegistry/docker/ecr.go @@ -0,0 +1,64 @@ +package docker + +import ( + "regexp" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/ecr" + "github.com/google/go-containerregistry/pkg/authn" + "github.com/pubg/kube-image-deployer/util" + "k8s.io/klog/v2" +) + +type ECRAuthenticator struct { + url string + region string +} + +func getRegionFromECRURL(url string) string { + return isECRRegex.FindStringSubmatch(url)[1] +} + +func (e *ECRAuthenticator) Authorization() (*authn.AuthConfig, error) { + + token, err := ecrCache.Get(e.url, func() (interface{}, error) { + + sess := session.Must(session.NewSessionWithOptions(session.Options{})) + svc := ecr.New(sess, aws.NewConfig().WithRegion(e.region)) + if token, err := svc.GetAuthorizationToken(&ecr.GetAuthorizationTokenInput{}); err != nil { + klog.Errorf("ECRAuthenticator GetAuthorizationToken error url=%s, err=%v", e.url, err) + return nil, err + } else { + klog.V(4).Infof("ECRAuthenticator GetAuthorizationToken success url=%s, token=%v", e.url, token) + return *token.AuthorizationData[0].AuthorizationToken, nil + } + }) + + if err != nil { + return nil, err + } + + c := &authn.AuthConfig{ + Auth: token.(string), + } + + return c, nil +} + +//aws_account_id.dkr.ecr.region.amazonaws.com +var isECRRegex = regexp.MustCompile(`\d+\.dkr\.ecr\.(.+)\.amazonaws.com`) +var ecrCache = util.NewCache(60 * 60 * 11) // 11 hours + +// isECR returns true if the url is ECR repository. +// ex> aws_account_id.dkr.ecr.region.amazonaws.com +func isECR(url string) bool { + return isECRRegex.Match([]byte(url)) +} + +func NewECRAuthenticator(url string) *ECRAuthenticator { + return &ECRAuthenticator{ + url: url, + region: getRegionFromECRURL(url), + } +} diff --git a/remoteRegistry/docker/private.go b/remoteRegistry/docker/private.go new file mode 100644 index 0000000..f074f99 --- /dev/null +++ b/remoteRegistry/docker/private.go @@ -0,0 +1,75 @@ +package docker + +import ( + "fmt" + + "github.com/google/go-containerregistry/pkg/authn" +) + +type PrivateAuthenticator struct { + url string + + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Auth string `json:"auth,omitempty"` + + // IdentityToken is used to authenticate the user and get + // an access token for the registry. + IdentityToken string `json:"identitytoken,omitempty"` + + // RegistryToken is a bearer token to be sent to a registry + RegistryToken string `json:"registrytoken,omitempty"` +} + +func (e *PrivateAuthenticator) Authorization() (*authn.AuthConfig, error) { + + if e.Username != "" && e.Password != "" { + return &authn.AuthConfig{ + Username: e.Username, + Password: e.Password, + }, nil + } else if e.IdentityToken != "" { + return &authn.AuthConfig{ + IdentityToken: e.IdentityToken, + }, nil + } else if e.RegistryToken != "" { + return &authn.AuthConfig{ + RegistryToken: e.RegistryToken, + }, nil + } else if e.Auth != "" { + return &authn.AuthConfig{ + Auth: e.Auth, + }, nil + } + + return nil, fmt.Errorf("no auth config found %+v", e) +} + +func NewPrivateAuthenticator(url, username, password string) *PrivateAuthenticator { + return &PrivateAuthenticator{ + url: url, + Username: username, + Password: password, + } +} + +func NewPrivateAuthenticatorWithIdentityToken(url, identityToken string) *PrivateAuthenticator { + return &PrivateAuthenticator{ + url: url, + IdentityToken: identityToken, + } +} + +func NewPrivateAuthenticatorWithRegistryToken(url, registryToken string) *PrivateAuthenticator { + return &PrivateAuthenticator{ + url: url, + RegistryToken: registryToken, + } +} + +func NewPrivateAuthenticatorWithAuth(url, auth string) *PrivateAuthenticator { + return &PrivateAuthenticator{ + url: url, + Auth: auth, + } +}