From e93544e42dd8c337b67c4930f26276bb886fbc42 Mon Sep 17 00:00:00 2001 From: Paulo Sousa Date: Mon, 16 Sep 2024 17:56:39 -0300 Subject: [PATCH] feat: add option to create remote repository before first push (#43) * feat: add option to create remote repository before first push fix lint * repository: change interface to only expose Ensure method * fix go.mod --- go.mod | 6 +- go.sum | 34 +++++- main.go | 17 ++- pkg/build/buildkit/build.go | 42 ++++++- pkg/build/buildkit/build_test.go | 24 ++++ pkg/build/grpc_build_v1/build_service.pb.go | 24 ++-- .../grpc_build_v1/build_service_grpc.pb.go | 15 ++- pkg/build/helpers.go | 14 +++ pkg/repository/fake/repository.go | 49 ++++++++ pkg/repository/oci/oci.go | 112 ++++++++++++++++++ pkg/repository/oci/oci_test.go | 109 +++++++++++++++++ pkg/repository/repository.go | 52 ++++++++ pkg/repository/repository_test.go | 44 +++++++ 13 files changed, 517 insertions(+), 25 deletions(-) create mode 100644 pkg/repository/fake/repository.go create mode 100644 pkg/repository/oci/oci.go create mode 100644 pkg/repository/oci/oci_test.go create mode 100644 pkg/repository/repository.go create mode 100644 pkg/repository/repository_test.go diff --git a/go.mod b/go.mod index 975ae73..d66045c 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/tsuru/deploy-agent -go 1.19 +go 1.21 require ( github.com/alessio/shellescape v1.4.1 @@ -9,7 +9,8 @@ require ( github.com/docker/docker v23.0.0-rc.1+incompatible github.com/google/go-containerregistry v0.12.0 github.com/moby/buildkit v0.11.3 - github.com/stretchr/testify v1.8.0 + github.com/oracle/oci-go-sdk/v65 v65.73.0 + github.com/stretchr/testify v1.8.4 golang.org/x/sync v0.7.0 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.1 @@ -61,6 +62,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect + github.com/sony/gobreaker v0.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/tonistiigi/fsutil v0.0.0-20230105215944-fb433841cbfa // indirect github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect diff --git a/go.sum b/go.sum index 20cfa8c..94cd4af 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,7 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= @@ -50,6 +51,7 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY= +github.com/Microsoft/hcsshim v0.9.6/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= @@ -75,7 +77,9 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw= +github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= +github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/containerd v1.6.16 h1:0H5xH6ABsN7XTrxIAKxFpBkFCBtrZ/OSORhCpUnHjrc= @@ -83,10 +87,14 @@ github.com/containerd/containerd v1.6.16/go.mod h1:1RdCUu95+gc2v9t3IL+zIlpClSmew github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= github.com/containerd/fifo v1.0.0 h1:6PirWBr9/L7GDamKr+XM0IeUFXu5mf3M/BPpH9gaLBU= +github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= github.com/containerd/nydus-snapshotter v0.3.1 h1:b8WahTrPkt3XsabjG2o/leN4fw3HWZYr+qxo/Z8Mfzk= +github.com/containerd/nydus-snapshotter v0.3.1/go.mod h1:+8R7NX7vrjlxAgtidnsstwIhpzyTlriYPssTxH++uiM= github.com/containerd/stargz-snapshotter v0.13.0 h1:3zr1/IkW1aEo6cMYTQeZ4L2jSuCN+F4kgGfjnuowe4U= github.com/containerd/stargz-snapshotter/estargz v0.13.0 h1:fD7AwuVV+B40p0d9qVkH/Au1qhp8hn/HWJHIYjpEcfw= +github.com/containerd/stargz-snapshotter/estargz v0.13.0/go.mod h1:m+9VaGJGlhCnrcEUod8mYumTmRgblwd3rC5UCEh2Yp0= github.com/containerd/ttrpc v1.1.0 h1:GbtyLRxb0gOLR0TYQWt3O6B0NvT8tMdorEHqIQo/lWI= +github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= github.com/containerd/typeurl v1.0.2 h1:Chlt8zIieDbzQFzXzAeBEF92KhExuE4p9p92/QmY7aY= github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= @@ -107,6 +115,7 @@ github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNk github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -122,6 +131,7 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= +github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -233,6 +243,7 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.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= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= @@ -253,6 +264,7 @@ github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/in-toto/in-toto-golang v0.5.0 h1:hb8bgwr0M2hGdDsLjkJ3ZqJ8JFLL/tgYdAxF/XEFBbY= +github.com/in-toto/in-toto-golang v0.5.0/go.mod h1:/Rq0IZHLV7Ku5gielPT4wPHJfH1GdHMCq8+WPxw8/BE= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -262,6 +274,7 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM= +github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -281,14 +294,17 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/moby/buildkit v0.11.3 h1:bnQFPHkNJTELRb2n3HISPGvB1FWzFx+YD1MTZg8bsfk= github.com/moby/buildkit v0.11.3/go.mod h1:P8MqGq7YrIDldCdZLhK8M/vPcrFYZ6GX1crX0j4hOmQ= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo= github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= +github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI= github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 h1:yH0SvLzcbZxcJXho2yh7CqdENGMQe73Cw3woZBpPli0= +github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -325,8 +341,12 @@ github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuh github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/oracle/oci-go-sdk/v65 v65.73.0 h1:C7uel6CoKk4A1KPkdhFBAyvVyFRTHAmX8m0o64RmfPg= +github.com/oracle/oci-go-sdk/v65 v65.73.0/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0= github.com/package-url/packageurl-go v0.1.1-0.20220428063043-89078438f170 h1:DiLBVp4DAcZlBVBEtJpNWZpZVq0AEeCY7Hqk8URVs4o= +github.com/package-url/packageurl-go v0.1.1-0.20220428063043-89078438f170/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -341,13 +361,18 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE= +github.com/secure-systems-lab/go-securesystemslib v0.4.0/go.mod h1:FGBZgq2tXWICsxWQW1msNf49F0Pf2Op5Htayx335Qbs= github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= +github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= +github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spdx/tools-golang v0.3.1-0.20230104082527-d6f58551be3f h1:9B623Cfs+mclYK6dsae7gLSwuIBHvlgmEup87qpqsAQ= +github.com/spdx/tools-golang v0.3.1-0.20230104082527-d6f58551be3f/go.mod h1:VHzvNsKAfAGqs4ZvwRL+7a0dNsL20s7lGui4K9C0xQM= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -356,6 +381,8 @@ github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -363,8 +390,9 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tonistiigi/fsutil v0.0.0-20230105215944-fb433841cbfa h1:XOFp/3aBXlqmOFAg3r6e0qQjPnK5I970LilqX+Is1W8= github.com/tonistiigi/fsutil v0.0.0-20230105215944-fb433841cbfa/go.mod h1:AvLEd1LEIl64G2Jpgwo7aVV5lGH0ePcKl0ygGIHNYl8= @@ -374,6 +402,7 @@ github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f h1:DLpt6B5oaaS8jy github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= +github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -572,6 +601,7 @@ golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -767,6 +797,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= @@ -787,6 +818,7 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/main.go b/main.go index 6b99870..3ad9e24 100644 --- a/main.go +++ b/main.go @@ -27,6 +27,7 @@ import ( "github.com/tsuru/deploy-agent/pkg/build/buildkit" buildpb "github.com/tsuru/deploy-agent/pkg/build/grpc_build_v1" "github.com/tsuru/deploy-agent/pkg/health" + "github.com/tsuru/deploy-agent/pkg/repository" ) const ( @@ -42,12 +43,13 @@ var cfg struct { BuildKitAutoDiscoveryKubernetesLeasePrefix string BuildKitAutoDiscoveryStatefulset string KubernetesConfig string - BuildKitAutoDiscoveryScaleGracefulPeriod time.Duration + RemoteRepositoryPath string BuildKitAutoDiscoveryTimeout time.Duration BuildKitAutoDiscoveryKubernetesPort int Port int ServerMaxRecvMsgSize int ServerMaxSendMsgSize int + BuildKitAutoDiscoveryScaleGracefulPeriod time.Duration BuildKitAutoDiscovery bool BuildKitAutoDiscoveryKubernetesSetTsuruAppLabels bool BuildKitAutoDiscoveryKubernetesUseSameNamespaceAsTsuruApp bool @@ -65,6 +67,8 @@ func main() { flag.StringVar(&cfg.BuildkitAddress, "buildkit-addr", getEnvOrDefault("BUILDKIT_HOST", ""), "Buildkit server address") flag.StringVar(&cfg.BuildkitTmpDir, "buildkit-tmp-dir", os.TempDir(), "Directory path to store temp files during container image builds") + flag.StringVar(&cfg.RemoteRepositoryPath, "remote-repository-path", getEnvOrDefault("REMOTE_REPOSITORY_PATH", ""), "Remote image repository providers config path") + flag.BoolVar(&cfg.BuildKitAutoDiscovery, "buildkit-autodiscovery", false, "Whether should dynamically discover the BuildKit service based on Tsuru app (if any)") flag.DurationVar(&cfg.BuildKitAutoDiscoveryTimeout, "buildkit-autodiscovery-timeout", (5 * time.Minute), "Max duration to discover an available BuildKit") flag.StringVar(&cfg.BuildKitAutoDiscoveryKubernetesPodSelector, "buildkit-autodiscovery-kubernetes-pod-selector", "", "Label selector of BuildKit's pods on Kubernetes") @@ -148,6 +152,17 @@ func newBuildKit() (*buildkit.BuildKit, error) { c = bc } + if cfg.RemoteRepositoryPath != "" { + repositoryData, err := os.ReadFile(cfg.RemoteRepositoryPath) + if err != nil { + return nil, err + } + opts.RemoteRepository, err = repository.NewRemoteRepository(repositoryData) + if err != nil { + return nil, fmt.Errorf("failed to handle remote repository cfg: %w", err) + } + } + b := buildkit.NewBuildKit(c, opts) if cfg.BuildKitAutoDiscovery { diff --git a/pkg/build/buildkit/build.go b/pkg/build/buildkit/build.go index 741e1e8..0391557 100644 --- a/pkg/build/buildkit/build.go +++ b/pkg/build/buildkit/build.go @@ -40,12 +40,14 @@ import ( "github.com/tsuru/deploy-agent/pkg/build" "github.com/tsuru/deploy-agent/pkg/build/buildkit/scaler" pb "github.com/tsuru/deploy-agent/pkg/build/grpc_build_v1" + repo "github.com/tsuru/deploy-agent/pkg/repository" "github.com/tsuru/deploy-agent/pkg/util" ) var _ build.Builder = (*BuildKit)(nil) type BuildKitOptions struct { + RemoteRepository map[string]repo.Repository TempDir string DiscoverBuildKitClientForApp bool } @@ -166,6 +168,13 @@ func (b *BuildKit) buildFromAppSourceFiles(ctx context.Context, c *client.Client } defer cleanFunc() + if b.opts.RemoteRepository != nil { + err = b.createRemoteRepository(ctx, r) + if err != nil { + return nil, err + } + } + if err = callBuildKitBuild(ctx, c, tmpDir, r, w); err != nil { return nil, err } @@ -222,6 +231,13 @@ func (b *BuildKit) buildFromContainerImage(ctx context.Context, c *client.Client } defer cleanFunc() + if b.opts.RemoteRepository != nil { + err = b.createRemoteRepository(ctx, r) + if err != nil { + return nil, err + } + } + if err = callBuildKitBuild(ctx, c, tmpDir, r, w); err != nil { return nil, err } @@ -255,6 +271,18 @@ func (b *BuildKit) extractTsuruConfigsFromContainerImage(ctx context.Context, c return callBuildKitToExtractTsuruConfigs(ctx, c, tmpDir, workingDir) } +func (b *BuildKit) createRemoteRepository(ctx context.Context, r *pb.BuildRequest) error { + for _, v := range r.DestinationImages { + if provider, ok := b.opts.RemoteRepository[build.GetRegistry(v)]; ok { + err := provider.Ensure(ctx, v) + if err != nil { + return err + } + } + } + return nil +} + func extractContainerImageConfigFromImageManifest(ctx context.Context, imageStr string, insecureRegistry bool) (*pb.ContainerImageConfig, error) { if err := ctx.Err(); err != nil { return nil, err @@ -398,6 +426,13 @@ func (b *BuildKit) buildFromContainerFile(ctx context.Context, c *client.Client, } defer cleanFunc() + if b.opts.RemoteRepository != nil { + err = b.createRemoteRepository(ctx, r) + if err != nil { + return nil, err + } + } + if err = callBuildKitBuild(ctx, c, tmpDir, r, w); err != nil { return nil, err } @@ -428,7 +463,12 @@ func (b *BuildKit) buildPlatform(ctx context.Context, c *client.Client, r *pb.Bu return err } defer cleanFunc() - + if b.opts.RemoteRepository != nil { + err = b.createRemoteRepository(ctx, r) + if err != nil { + return err + } + } return callBuildKitBuild(ctx, c, tmpDir, r, w) } diff --git a/pkg/build/buildkit/build_test.go b/pkg/build/buildkit/build_test.go index bec70b5..6e1ae38 100644 --- a/pkg/build/buildkit/build_test.go +++ b/pkg/build/buildkit/build_test.go @@ -26,6 +26,8 @@ import ( . "github.com/tsuru/deploy-agent/pkg/build/buildkit" pb "github.com/tsuru/deploy-agent/pkg/build/grpc_build_v1" + "github.com/tsuru/deploy-agent/pkg/repository" + "github.com/tsuru/deploy-agent/pkg/repository/fake" "github.com/tsuru/deploy-agent/pkg/util" ) @@ -376,6 +378,28 @@ func TestBuildKit_Build_FromContainerImages(t *testing.T) { }, appFiles) }) + t.Run("creating remote repository", func(t *testing.T) { + req := &pb.BuildRequest{ + Kind: pb.BuildKind_BUILD_KIND_APP_DEPLOY_WITH_CONTAINER_IMAGE, + App: &pb.TsuruApp{ + Name: "my-app", + }, + SourceImage: "nginx:1.22-alpine", + DestinationImages: []string{baseRegistry(t, "app-my-app", "v1")}, + PushOptions: &pb.PushOptions{InsecureRegistry: registryHTTP}, + } + opts := &BuildKitOptions{TempDir: t.TempDir(), RemoteRepository: map[string]repository.Repository{registryAddress: &fake.FakeRepository{}}} + assert.Equal(t, opts.RemoteRepository[registryAddress].(*fake.FakeRepository).RepoExists, map[string]bool(nil)) + _, err := NewBuildKit(bc, *opts). + Build(context.TODO(), req, os.Stdout) + require.NoError(t, err) + assert.Equal(t, opts.RemoteRepository[registryAddress].(*fake.FakeRepository).RepoExists, map[string]bool{baseRegistry(t, "app-my-app", "v1"): true}) + _, err = NewBuildKit(bc, *opts). + Build(context.TODO(), req, os.Stdout) + require.NoError(t, err) + assert.Equal(t, opts.RemoteRepository[registryAddress].(*fake.FakeRepository).RepoExists, map[string]bool{baseRegistry(t, "app-my-app", "v1"): true}) + }) + t.Run("container image without Tsuru app files (tsuru.yaml, Procfile) + job image push", func(t *testing.T) { req := &pb.BuildRequest{ Kind: pb.BuildKind_BUILD_KIND_JOB_CREATE_WITH_CONTAINER_IMAGE, diff --git a/pkg/build/grpc_build_v1/build_service.pb.go b/pkg/build/grpc_build_v1/build_service.pb.go index b1db89b..ef4fe15 100644 --- a/pkg/build/grpc_build_v1/build_service.pb.go +++ b/pkg/build/grpc_build_v1/build_service.pb.go @@ -4,8 +4,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.34.2 -// protoc v5.27.1 +// protoc-gen-go v1.31.0 +// protoc v5.27.3 // source: pkg/build/grpc_build_v1/build_service.proto package grpc_build_v1 @@ -820,7 +820,7 @@ func file_pkg_build_grpc_build_v1_build_service_proto_rawDescGZIP() []byte { var file_pkg_build_grpc_build_v1_build_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_pkg_build_grpc_build_v1_build_service_proto_msgTypes = make([]protoimpl.MessageInfo, 10) -var file_pkg_build_grpc_build_v1_build_service_proto_goTypes = []any{ +var file_pkg_build_grpc_build_v1_build_service_proto_goTypes = []interface{}{ (BuildKind)(0), // 0: grpc_build_v1.BuildKind (*BuildRequest)(nil), // 1: grpc_build_v1.BuildRequest (*BuildResponse)(nil), // 2: grpc_build_v1.BuildResponse @@ -858,7 +858,7 @@ func file_pkg_build_grpc_build_v1_build_service_proto_init() { return } if !protoimpl.UnsafeEnabled { - file_pkg_build_grpc_build_v1_build_service_proto_msgTypes[0].Exporter = func(v any, i int) any { + file_pkg_build_grpc_build_v1_build_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BuildRequest); i { case 0: return &v.state @@ -870,7 +870,7 @@ func file_pkg_build_grpc_build_v1_build_service_proto_init() { return nil } } - file_pkg_build_grpc_build_v1_build_service_proto_msgTypes[1].Exporter = func(v any, i int) any { + file_pkg_build_grpc_build_v1_build_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BuildResponse); i { case 0: return &v.state @@ -882,7 +882,7 @@ func file_pkg_build_grpc_build_v1_build_service_proto_init() { return nil } } - file_pkg_build_grpc_build_v1_build_service_proto_msgTypes[2].Exporter = func(v any, i int) any { + file_pkg_build_grpc_build_v1_build_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TsuruApp); i { case 0: return &v.state @@ -894,7 +894,7 @@ func file_pkg_build_grpc_build_v1_build_service_proto_init() { return nil } } - file_pkg_build_grpc_build_v1_build_service_proto_msgTypes[3].Exporter = func(v any, i int) any { + file_pkg_build_grpc_build_v1_build_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TsuruJob); i { case 0: return &v.state @@ -906,7 +906,7 @@ func file_pkg_build_grpc_build_v1_build_service_proto_init() { return nil } } - file_pkg_build_grpc_build_v1_build_service_proto_msgTypes[4].Exporter = func(v any, i int) any { + file_pkg_build_grpc_build_v1_build_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TsuruPlatform); i { case 0: return &v.state @@ -918,7 +918,7 @@ func file_pkg_build_grpc_build_v1_build_service_proto_init() { return nil } } - file_pkg_build_grpc_build_v1_build_service_proto_msgTypes[5].Exporter = func(v any, i int) any { + file_pkg_build_grpc_build_v1_build_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PushOptions); i { case 0: return &v.state @@ -930,7 +930,7 @@ func file_pkg_build_grpc_build_v1_build_service_proto_init() { return nil } } - file_pkg_build_grpc_build_v1_build_service_proto_msgTypes[6].Exporter = func(v any, i int) any { + file_pkg_build_grpc_build_v1_build_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ContainerImageConfig); i { case 0: return &v.state @@ -942,7 +942,7 @@ func file_pkg_build_grpc_build_v1_build_service_proto_init() { return nil } } - file_pkg_build_grpc_build_v1_build_service_proto_msgTypes[7].Exporter = func(v any, i int) any { + file_pkg_build_grpc_build_v1_build_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TsuruConfig); i { case 0: return &v.state @@ -955,7 +955,7 @@ func file_pkg_build_grpc_build_v1_build_service_proto_init() { } } } - file_pkg_build_grpc_build_v1_build_service_proto_msgTypes[1].OneofWrappers = []any{ + file_pkg_build_grpc_build_v1_build_service_proto_msgTypes[1].OneofWrappers = []interface{}{ (*BuildResponse_Output)(nil), (*BuildResponse_TsuruConfig)(nil), } diff --git a/pkg/build/grpc_build_v1/build_service_grpc.pb.go b/pkg/build/grpc_build_v1/build_service_grpc.pb.go index d790d7c..ceaeb50 100644 --- a/pkg/build/grpc_build_v1/build_service_grpc.pb.go +++ b/pkg/build/grpc_build_v1/build_service_grpc.pb.go @@ -4,8 +4,8 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 -// - protoc v5.27.1 +// - protoc-gen-go-grpc v1.3.0 +// - protoc v5.27.3 // source: pkg/build/grpc_build_v1/build_service.proto package grpc_build_v1 @@ -19,8 +19,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 const ( Build_Build_FullMethodName = "/grpc_build_v1.Build/Build" @@ -43,12 +43,11 @@ func NewBuildClient(cc grpc.ClientConnInterface) BuildClient { } func (c *buildClient) Build(ctx context.Context, in *BuildRequest, opts ...grpc.CallOption) (Build_BuildClient, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &Build_ServiceDesc.Streams[0], Build_Build_FullMethodName, cOpts...) + stream, err := c.cc.NewStream(ctx, &Build_ServiceDesc.Streams[0], Build_Build_FullMethodName, opts...) if err != nil { return nil, err } - x := &buildBuildClient{ClientStream: stream} + x := &buildBuildClient{stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } @@ -109,7 +108,7 @@ func _Build_Build_Handler(srv interface{}, stream grpc.ServerStream) error { if err := stream.RecvMsg(m); err != nil { return err } - return srv.(BuildServer).Build(m, &buildBuildServer{ServerStream: stream}) + return srv.(BuildServer).Build(m, &buildBuildServer{stream}) } type Build_BuildServer interface { diff --git a/pkg/build/helpers.go b/pkg/build/helpers.go index b8fe661..2f926d4 100644 --- a/pkg/build/helpers.go +++ b/pkg/build/helpers.go @@ -293,3 +293,17 @@ func SortExposedPorts(ports map[string]struct{}) []string { return ps } + +func GetRegistry(image string) string { + defaultRegistry := "docker.io" + if !strings.Contains(image, "/") { + return defaultRegistry + } + + registry := strings.Split(image, "/")[0] + if strings.Contains(registry, ".") || strings.Contains(registry, ":") { + return registry + } + + return defaultRegistry +} diff --git a/pkg/repository/fake/repository.go b/pkg/repository/fake/repository.go new file mode 100644 index 0000000..fad1bd7 --- /dev/null +++ b/pkg/repository/fake/repository.go @@ -0,0 +1,49 @@ +// Copyright 2024 tsuru authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fake + +import ( + "context" + "errors" +) + +type FakeRepository struct { + CreatedRepos map[string]bool + RepoExists map[string]bool +} + +func (f *FakeRepository) create(name string) error { + if _, exists := f.RepoExists[name]; exists { + return errors.New("repository already exists") + } + if f.CreatedRepos == nil { + f.CreatedRepos = make(map[string]bool) + } + if f.RepoExists == nil { + f.RepoExists = make(map[string]bool) + } + f.CreatedRepos[name] = true + f.RepoExists[name] = true + return nil +} + +func (f *FakeRepository) exists(name string) (bool, error) { + if f.RepoExists == nil { + f.RepoExists = make(map[string]bool) + } + exists := f.RepoExists[name] + return exists, nil +} + +func (f *FakeRepository) Ensure(ctx context.Context, name string) error { + exists, err := f.exists(name) + if err != nil { + return err + } + if !exists { + return f.create(name) + } + return nil +} diff --git a/pkg/repository/oci/oci.go b/pkg/repository/oci/oci.go new file mode 100644 index 0000000..2bdde3a --- /dev/null +++ b/pkg/repository/oci/oci.go @@ -0,0 +1,112 @@ +// Copyright 2024 tsuru authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package oci + +import ( + "context" + "fmt" + "strings" + + "github.com/oracle/oci-go-sdk/v65/artifacts" + "github.com/oracle/oci-go-sdk/v65/common" +) + +type OCIRequiredMethods interface { + CreateContainerRepository(ctx context.Context, request artifacts.CreateContainerRepositoryRequest) (response artifacts.CreateContainerRepositoryResponse, err error) + ListContainerRepositories(ctx context.Context, request artifacts.ListContainerRepositoriesRequest) (response artifacts.ListContainerRepositoriesResponse, err error) +} + +type OCI struct { + client OCIRequiredMethods + CompartmentID string + Profile string + ConfigPath string +} + +func NewOCI(data map[string]string) *OCI { + return &OCI{ + CompartmentID: data["compartmentID"], + Profile: data["profile"], + ConfigPath: data["configPath"], + client: &artifacts.ArtifactsClient{}, + } +} + +func (r *OCI) Ensure(ctx context.Context, name string) error { + err := r.auth(ctx) + if err != nil { + return err + } + exists, err := r.exists(ctx, name) + if err != nil { + return err + } + if !exists { + return r.create(ctx, name) + } + return nil +} + +func (r *OCI) auth(ctx context.Context) error { + if r.client != nil { + return nil + } + configProvider := common.CustomProfileConfigProvider(r.ConfigPath, r.Profile) + client, err := artifacts.NewArtifactsClientWithConfigurationProvider(configProvider) + r.client = &client + if err != nil { + return err + } + return err +} + +func (r *OCI) create(ctx context.Context, name string) error { + name, err := parserRegistryRepository(name) + if err != nil { + return err + } + request := artifacts.CreateContainerRepositoryRequest{ + CreateContainerRepositoryDetails: artifacts.CreateContainerRepositoryDetails{ + CompartmentId: &r.CompartmentID, + DisplayName: common.String(name), + }, + } + _, err = r.client.CreateContainerRepository(ctx, request) + if err != nil { + return err + } + return nil +} + +func (r *OCI) exists(ctx context.Context, name string) (bool, error) { + name, err := parserRegistryRepository(name) + if err != nil { + return false, err + } + request := artifacts.ListContainerRepositoriesRequest{ + CompartmentId: &r.CompartmentID, + DisplayName: common.String(name), + } + response, err := r.client.ListContainerRepositories(ctx, request) + if err != nil { + return false, err + } + if len(response.ContainerRepositoryCollection.Items) == 0 { + return false, nil + } + return true, nil +} + +func parserRegistryRepository(image string) (string, error) { + parts := strings.Split(image, "/") + if len(parts) < 3 { + return "", fmt.Errorf("invalid image format %s", image) + } + repoWithTag := strings.Join(parts[2:], "/") + repoParts := strings.Split(repoWithTag, ":") + repoWithoutTag := repoParts[0] + + return repoWithoutTag, nil +} diff --git a/pkg/repository/oci/oci_test.go b/pkg/repository/oci/oci_test.go new file mode 100644 index 0000000..6ac6548 --- /dev/null +++ b/pkg/repository/oci/oci_test.go @@ -0,0 +1,109 @@ +// Copyright 2024 tsuru authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package oci + +import ( + "context" + "errors" + "testing" + + "github.com/oracle/oci-go-sdk/v65/artifacts" + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/stretchr/testify/assert" +) + +type FakeArtifactsClient struct { + repo map[string]bool + artifacts.ArtifactsClient +} + +func (m *FakeArtifactsClient) CreateContainerRepository(ctx context.Context, request artifacts.CreateContainerRepositoryRequest) (response artifacts.CreateContainerRepositoryResponse, err error) { + repo := *request.CreateContainerRepositoryDetails.DisplayName + if _, ok := m.repo[repo]; ok { + return artifacts.CreateContainerRepositoryResponse{}, errors.New("repository already exists") + } + if m.repo == nil { + m.repo = make(map[string]bool) + } + m.repo[repo] = true + return artifacts.CreateContainerRepositoryResponse{}, nil +} + +func (m *FakeArtifactsClient) ListContainerRepositories(ctx context.Context, request artifacts.ListContainerRepositoriesRequest) (response artifacts.ListContainerRepositoriesResponse, err error) { + repos := make([]artifacts.ContainerRepositorySummary, 0, 10) + for repo := range m.repo { + repos = append(repos, artifacts.ContainerRepositorySummary{DisplayName: common.String(repo)}) + } + return artifacts.ListContainerRepositoriesResponse{ + ContainerRepositoryCollection: artifacts.ContainerRepositoryCollection{ + Items: repos, + }, + }, nil +} + +func TestOCI_Ensure(t *testing.T) { + fakeClient := new(FakeArtifactsClient) + oci := &OCI{ + client: fakeClient, + } + ctx := context.TODO() + name := "registry/namespace/test-repo" + exists, err := oci.exists(ctx, name) + assert.NoError(t, err) + assert.False(t, exists) + err = oci.Ensure(ctx, name) + assert.NoError(t, err) + exists, err = oci.exists(ctx, name) + assert.NoError(t, err) + assert.True(t, exists) + err = oci.create(ctx, name) + assert.Error(t, err, "repository already exists") +} + +func TestParserRegistryRepository(t *testing.T) { + tests := []struct { + name string + image string + expected string + expectedErr bool + }{ + { + name: "No slash", + image: "image", + expected: "", + expectedErr: true, + }, + { + name: "One slash", + image: "registry/image", + expected: "", + expectedErr: true, + }, + { + name: "Multiple slashes", + image: "registry/namespace/image", + expected: "image", + expectedErr: false, + }, + { + name: "With tag", + image: "registry/namespace/image:tag", + expected: "image", + expectedErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := parserRegistryRepository(tt.image) + if tt.expectedErr { + assert.Error(t, err) + return + } + assert.NoError(t, err) + assert.Equal(t, tt.expected, result) + }) + } +} diff --git a/pkg/repository/repository.go b/pkg/repository/repository.go new file mode 100644 index 0000000..8acaa68 --- /dev/null +++ b/pkg/repository/repository.go @@ -0,0 +1,52 @@ +// Copyright 2024 tsuru authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package repository + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/tsuru/deploy-agent/pkg/repository/fake" + "github.com/tsuru/deploy-agent/pkg/repository/oci" +) + +type Repository interface { + Ensure(ctx context.Context, name string) error +} + +type RemoteRepositoryProvider map[string]map[string]string + +func repositoryProvider(providerType string, data map[string]string) (Repository, error) { + switch providerType { + case "oci": + return oci.NewOCI(data), nil + case "fake": + return &fake.FakeRepository{}, nil + default: + return nil, fmt.Errorf("unknow repositoy provider: %s", providerType) + } +} + +func NewRemoteRepository(data []byte) (map[string]Repository, error) { + var remoteRepositoryProvider RemoteRepositoryProvider + err := json.Unmarshal(data, &remoteRepositoryProvider) + if err != nil { + return nil, err + } + var repositoryMap = make(map[string]Repository) + for k, v := range remoteRepositoryProvider { + if p, ok := v["provider"]; ok { + provider, err := repositoryProvider(p, v) + if err != nil { + return nil, err + } + repositoryMap[k] = provider + continue + } + return nil, fmt.Errorf("provider key not found in repository %s", k) + } + return repositoryMap, nil +} diff --git a/pkg/repository/repository_test.go b/pkg/repository/repository_test.go new file mode 100644 index 0000000..e84b435 --- /dev/null +++ b/pkg/repository/repository_test.go @@ -0,0 +1,44 @@ +// Copyright 2024 tsuru authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package repository + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tsuru/deploy-agent/pkg/repository/fake" + "github.com/tsuru/deploy-agent/pkg/repository/oci" +) + +func TestNewRemoteRepository(t *testing.T) { + data := []byte(`{ + "test.com": { + "provider": "oci", + "compartmentID": "123", + "profile": "dev" + }, + "faker.com": { + "provider": "fake" + } + }`) + repositoryMap, err := NewRemoteRepository(data) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + assert.Len(t, repositoryMap, 2) + assert.Equal(t, oci.NewOCI(map[string]string{"compartmentID": "123", "profile": "dev"}), repositoryMap["test.com"]) + assert.Equal(t, &fake.FakeRepository{}, repositoryMap["faker.com"].(*fake.FakeRepository)) +} + +func TestNewRepositoryInvalidProvider(t *testing.T) { + data := []byte(`{ + "test.com": { + "provider": "invalid" + } + }`) + _, err := NewRemoteRepository(data) + assert.Error(t, err) + assert.Equal(t, "unknow repositoy provider: invalid", err.Error()) +}