Skip to content

Commit 394059f

Browse files
committed
Add go-containerregistry and implement SyncImage
1 parent c1d940c commit 394059f

File tree

4 files changed

+165
-8
lines changed

4 files changed

+165
-8
lines changed

experiments/tibuild-v2/go.mod

+12-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/PingCAP-QE/ee-apps/tibuild
33
go 1.24.0
44

55
require (
6+
github.com/google/go-containerregistry v0.20.3
67
github.com/rs/zerolog v1.33.0
78
goa.design/clue v1.1.0
89
goa.design/goa/v3 v3.20.0
@@ -12,20 +13,30 @@ require (
1213

1314
require (
1415
github.com/aws/smithy-go v1.22.2 // indirect
16+
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
1517
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
1618
github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 // indirect
19+
github.com/docker/cli v27.5.0+incompatible // indirect
20+
github.com/docker/distribution v2.8.3+incompatible // indirect
21+
github.com/docker/docker-credential-helpers v0.8.2 // indirect
1722
github.com/go-chi/chi/v5 v5.2.1 // indirect
1823
github.com/go-logr/logr v1.4.2 // indirect
19-
github.com/gohugoio/hashstructure v0.5.0 // indirect
2024
github.com/google/uuid v1.6.0 // indirect
2125
github.com/gorilla/websocket v1.5.3 // indirect
26+
github.com/klauspost/compress v1.17.11 // indirect
2227
github.com/kr/pretty v0.3.1 // indirect
2328
github.com/manveru/faker v0.0.0-20171103152722-9fbc68a78c4d // indirect
2429
github.com/mattn/go-colorable v0.1.13 // indirect
2530
github.com/mattn/go-isatty v0.0.19 // indirect
31+
github.com/mitchellh/go-homedir v1.1.0 // indirect
32+
github.com/opencontainers/go-digest v1.0.0 // indirect
33+
github.com/opencontainers/image-spec v1.1.0 // indirect
34+
github.com/pkg/errors v0.9.1 // indirect
2635
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
2736
github.com/rogpeppe/go-internal v1.12.0 // indirect
37+
github.com/sirupsen/logrus v1.9.3 // indirect
2838
github.com/stretchr/testify v1.10.0 // indirect
39+
github.com/vbatts/tar-split v0.11.6 // indirect
2940
go.opentelemetry.io/otel v1.34.0 // indirect
3041
go.opentelemetry.io/otel/trace v1.34.0 // indirect
3142
golang.org/x/mod v0.23.0 // indirect

experiments/tibuild-v2/go.sum

+32-2
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,40 @@
11
github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
22
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
3+
github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8=
4+
github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU=
35
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
46
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
7+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
59
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
610
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
711
github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 h1:MGKhKyiYrvMDZsmLR/+RGffQSXwEkXgfLSA08qDn9AI=
812
github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598/go.mod h1:0FpDmbrt36utu8jEmeU05dPC9AB5tsLYVVi+ZHfyuwI=
13+
github.com/docker/cli v27.5.0+incompatible h1:aMphQkcGtpHixwwhAXJT1rrK/detk2JIvDaFkLctbGM=
14+
github.com/docker/cli v27.5.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
15+
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
16+
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
17+
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
18+
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
919
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
1020
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
1121
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
1222
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
1323
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
1424
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
1525
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
16-
github.com/gohugoio/hashstructure v0.5.0 h1:G2fjSBU36RdwEJBWJ+919ERvOVqAg9tfcYp47K9swqg=
17-
github.com/gohugoio/hashstructure v0.5.0/go.mod h1:Ser0TniXuu/eauYmrwM4o64EBvySxNzITEOLlm4igec=
1826
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
1927
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
2028
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
2129
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
30+
github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI=
31+
github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI=
2232
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
2333
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
2434
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
2535
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
36+
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
37+
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
2638
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
2739
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
2840
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -39,8 +51,16 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
3951
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
4052
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
4153
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
54+
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
55+
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
56+
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
57+
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
58+
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
59+
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
4260
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
61+
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
4362
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
63+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
4464
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
4565
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
4666
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
@@ -49,8 +69,14 @@ github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99
4969
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
5070
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
5171
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
72+
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
73+
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
74+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
75+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
5276
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
5377
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
78+
github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs=
79+
github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI=
5480
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
5581
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
5682
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
@@ -77,6 +103,7 @@ golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
77103
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
78104
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
79105
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
106+
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
80107
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
81108
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
82109
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -97,5 +124,8 @@ google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojt
97124
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
98125
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
99126
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
127+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
100128
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
101129
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
130+
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
131+
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=

experiments/tibuild-v2/pkg/impl/artifact.go

+40-5
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ package impl
22

33
import (
44
"context"
5+
"fmt"
6+
"net/http"
57

6-
artifact "github.com/PingCAP-QE/ee-apps/tibuild/gen/artifact"
8+
"github.com/google/go-containerregistry/pkg/crane"
79
"github.com/rs/zerolog"
10+
11+
artifact "github.com/PingCAP-QE/ee-apps/tibuild/gen/artifact"
812
)
913

1014
// artifact service example implementation.
@@ -20,9 +24,40 @@ func NewArtifact(logger *zerolog.Logger) artifact.Service {
2024
}
2125
}
2226

23-
// Sync hotfix image to dockerhub
27+
// SyncImage copies a Docker image from the source registry to the target registry.
28+
//
29+
// When running in k8s pod, it should use the service account that has Docker authentication
30+
// configured and appended to its context.
31+
//
32+
// When debugging locally, it will use the default authentication stored in the
33+
// Docker config.json file (~/.docker/config.json).
2434
func (s *artifactsrvc) SyncImage(ctx context.Context, p *artifact.ImageSyncRequest) (res *artifact.ImageSyncRequest, err error) {
25-
res = &artifact.ImageSyncRequest{}
26-
s.logger.Info().Msgf("artifact.syncImage")
27-
return
35+
// skip validate input parameters, since the http access layer will do the validation.
36+
37+
l := s.logger.With().
38+
Str("source", p.Source).
39+
Str("target", p.Target).
40+
Logger()
41+
42+
l.Info().Msg("Syncing Docker image")
43+
44+
// Create options for crane operations
45+
options := []crane.Option{
46+
crane.WithContext(ctx),
47+
}
48+
49+
// Use the crane library to copy the image directly between registries
50+
if err := crane.Copy(p.Source, p.Target, options...); err != nil {
51+
l.Err(err).Msg("Failed to sync image")
52+
53+
return nil, &artifact.HTTPError{
54+
Code: http.StatusInternalServerError,
55+
Message: fmt.Sprintf("failed to sync image: %v", err),
56+
}
57+
}
58+
59+
l.Info().Msg("Image successfully synced to DockerHub")
60+
61+
res = p
62+
return res, nil
2863
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package impl_test
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
"time"
8+
9+
"github.com/PingCAP-QE/ee-apps/tibuild/gen/artifact"
10+
"github.com/PingCAP-QE/ee-apps/tibuild/pkg/impl"
11+
"github.com/rs/zerolog"
12+
"github.com/stretchr/testify/assert"
13+
)
14+
15+
func TestSyncImage_Integration(t *testing.T) {
16+
// Skip if running in CI or want to skip integration tests
17+
if testing.Short() {
18+
t.Skip("Skipping integration test")
19+
}
20+
21+
logger := zerolog.New(zerolog.NewConsoleWriter()).With().Timestamp().Logger()
22+
service := impl.NewArtifact(&logger)
23+
24+
t.Run("sync real image to ttl.sh", func(t *testing.T) {
25+
// Setup
26+
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
27+
defer cancel()
28+
29+
// Use a small image for faster tests
30+
sourceImage := "alpine:latest"
31+
32+
// ttl.sh creates a random repository with 24h expiration
33+
// Use a unique name to avoid conflicts
34+
randomSuffix := time.Now().UnixNano()
35+
targetImage := fmt.Sprintf("ttl.sh/test-sync-%d:1h", randomSuffix)
36+
37+
req := &artifact.ImageSyncRequest{
38+
Source: sourceImage,
39+
Target: targetImage,
40+
}
41+
42+
// Execute
43+
resp, err := service.SyncImage(ctx, req)
44+
45+
// Verify
46+
assert.NoError(t, err)
47+
assert.Equal(t, req, resp)
48+
49+
// Log the target image for manual verification if needed
50+
t.Logf("Successfully pushed image to: %s", targetImage)
51+
52+
// Optional: Verify the image exists in the registry
53+
// This would require additional code to check if the image was pushed correctly
54+
})
55+
56+
t.Run("sync non-existent image should fail", func(t *testing.T) {
57+
// Setup
58+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
59+
defer cancel()
60+
61+
sourceImage := "debian:non-existent-tag-12345"
62+
targetImage := "ttl.sh/test-sync-should-fail:1h"
63+
64+
req := &artifact.ImageSyncRequest{
65+
Source: sourceImage,
66+
Target: targetImage,
67+
}
68+
69+
// Execute
70+
resp, err := service.SyncImage(ctx, req)
71+
72+
// Verify
73+
assert.Error(t, err)
74+
assert.Nil(t, resp)
75+
76+
// Check that we got an HTTP error
77+
httpErr, ok := err.(*artifact.HTTPError)
78+
assert.True(t, ok)
79+
assert.Contains(t, httpErr.Message, "failed to sync image")
80+
})
81+
}

0 commit comments

Comments
 (0)