Skip to content

Commit

Permalink
pref(devbox): pref the release retag
Browse files Browse the repository at this point in the history
Signed-off-by: cbluebird <[email protected]>
  • Loading branch information
cbluebird committed Dec 24, 2024
1 parent cbe7170 commit a872a01
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 129 deletions.
2 changes: 1 addition & 1 deletion controllers/devbox/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ func main() {
if err = (&controller.DevBoxReleaseReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Registry: &registry.Client{
Registry: &registry.Registry{
Username: registryUser,
Password: registryPassword,
},
Expand Down
5 changes: 3 additions & 2 deletions controllers/devbox/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/google/go-containerregistry v0.20.2
github.com/onsi/ginkgo/v2 v2.17.1
github.com/onsi/gomega v1.32.0
github.com/regclient/regclient v0.8.0
golang.org/x/crypto v0.21.0
k8s.io/api v0.30.1
k8s.io/apimachinery v0.30.1
Expand Down Expand Up @@ -75,8 +76,8 @@ require (
golang.org/x/net v0.23.0 // indirect
golang.org/x/oauth2 v0.12.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/term v0.27.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.18.0 // indirect
Expand Down
10 changes: 6 additions & 4 deletions controllers/devbox/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdO
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/regclient/regclient v0.8.0 h1:xNAMDlADcyMvFAlGXoqDOxlSUBG4mqWBFgjQqVTP8Og=
github.com/regclient/regclient v0.8.0/go.mod h1:h9+Y6dBvqBkdlrj6EIhbTOv0xUuIFl7CdI1bZvEB42g=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
Expand Down Expand Up @@ -184,10 +186,10 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
Expand Down
33 changes: 23 additions & 10 deletions controllers/devbox/internal/controller/devboxrelease_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,17 @@ import (
// DevBoxReleaseReconciler reconciles a DevBoxRelease object
type DevBoxReleaseReconciler struct {
client.Client
Registry *registry.Client
Registry *registry.Registry
Scheme *runtime.Scheme
}

type ImageInfo struct {
TotalName string
Repository string
ImageName string
Tag string
}

// +kubebuilder:rbac:groups=devbox.sealos.io,resources=devboxreleases,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=devbox.sealos.io,resources=devboxreleases/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=devbox.sealos.io,resources=devboxreleases/finalizers,verbs=update
Expand Down Expand Up @@ -113,38 +120,44 @@ func (r *DevBoxReleaseReconciler) CreateReleaseTag(ctx context.Context, devboxRe
if err := r.Get(ctx, devboxInfo, devbox); err != nil {
return err
}
hostName, imageName, oldTag, err := r.GetImageInfo(devbox)
imageInfo, err := r.GetImageRef(devbox)
if err != nil {
return err
}
logger.Info("Tagging image", "host", hostName, "image", imageName, "oldTag", oldTag, "newTag", devboxRelease.Spec.NewTag)
devboxRelease.Status.OriginalImage = imageName + ":" + oldTag
logger.Info("Tagging image", "host", imageInfo.Repository, "image", imageInfo.ImageName, "oldTag", imageInfo.Tag, "newTag", devboxRelease.Spec.NewTag)
devboxRelease.Status.OriginalImage = fmt.Sprintf("%s:%s", imageInfo.ImageName, imageInfo.Tag)
if err = r.Status().Update(ctx, devboxRelease); err != nil {
logger.Error(err, "Failed to update status", "devbox", devboxRelease.Spec.DevboxName, "newTag", devboxRelease.Spec.NewTag)
return err
}
return r.Registry.TagImage(hostName, imageName, oldTag, devboxRelease.Spec.NewTag)
return r.Registry.Tag(imageInfo.TotalName, fmt.Sprintf("%s/%s:%s", imageInfo.Repository, imageInfo.ImageName, devboxRelease.Spec.NewTag))
}

func (r *DevBoxReleaseReconciler) DeleteReleaseTag(_ context.Context, _ *devboxv1alpha1.DevBoxRelease) error {
//todo only delete CR without doing any other operations
return nil
}

func (r *DevBoxReleaseReconciler) GetImageInfo(devbox *devboxv1alpha1.Devbox) (string, string, string, error) {
func (r *DevBoxReleaseReconciler) GetImageRef(devbox *devboxv1alpha1.Devbox) (ImageInfo, error) {
if len(devbox.Status.CommitHistory) == 0 {
return "", "", "", fmt.Errorf("commit history is empty")
return ImageInfo{}, fmt.Errorf("commit history is empty")
}
commitHistory := helper.GetLastSuccessCommitHistory(devbox)
if commitHistory == nil {
return "", "", "", fmt.Errorf("no successful commit history found")
return ImageInfo{}, fmt.Errorf("no successful commit history found")
}

res, err := reference.ParseReference(commitHistory.Image)
if err != nil {
return "", "", "", err
return ImageInfo{}, err
}
repo := res.Context()
return repo.RegistryStr(), repo.RepositoryStr(), res.Identifier(), nil
return ImageInfo{
TotalName: commitHistory.Image,
Repository: repo.RegistryStr(),
ImageName: repo.RepositoryStr(),
Tag: res.Identifier(),
}, nil
}

// SetupWithManager sets up the controller with the Manager.
Expand Down
138 changes: 36 additions & 102 deletions controllers/devbox/internal/controller/utils/registry/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,128 +15,62 @@
package registry

import (
"bytes"
"context"
"errors"
"io"
"net/http"
"time"

"github.com/avast/retry-go"
"github.com/google/go-containerregistry/pkg/name"
registry "github.com/regclient/regclient"
"github.com/regclient/regclient/config"
"github.com/regclient/regclient/types/ref"
)

type Client struct {
Username string
Password string
}

var (
ErrorManifestNotFound = errors.New("manifest not found")
)

func (t *Client) TagImage(hostName string, imageName string, oldTag string, newTag string) error {
return retry.Do(func() error {
manifest, err := t.pullManifest(t.Username, t.Password, hostName, imageName, oldTag)
if err != nil {
return err
}
return t.pushManifest(t.Username, t.Password, hostName, imageName, newTag, manifest)
}, retry.Delay(time.Second*5), retry.Attempts(3), retry.LastErrorOnly(true))
type Registry struct {
Username string
Password string
}

//func (t *Client) login(authPath string, username string, password string, imageName string) (string, error) {
// var (
// client = http.DefaultClient
// url = authPath + imageName + ":pull,push"
// )
//
// req, err := http.NewRequest("GET", url, nil)
// if err != nil {
// return "", err
// }
//
// req.SetBasicAuth(username, password)
//
// resp, err := client.Do(req)
// if err != nil {
// return "", err
// }
//
// if resp.StatusCode != http.StatusOK {
// return "", errors.New(resp.Status)
// }
//
// bodyText, err := ioutil.ReadAll(resp.Body)
// if err != nil {
// return "", err
// }
// var data struct {
// Token string `json:"token"`
// AccessToken string `json:"access_token"`
// ExpiresIn int `json:"expires_in"`
// IssuedAt string `json:"issued_at"`
// }
// if err := json.Unmarshal(bodyText, &data); err != nil {
// return "", err
// }
// if data.Token == "" {
// return "", errors.New("empty token")
// }
// return data.Token, nil
//}

func (t *Client) pullManifest(username string, password string, hostName string, imageName string, tag string) ([]byte, error) {
var (
client = http.DefaultClient
url = "http://" + hostName + "/v2/" + imageName + "/manifests/" + tag
)
req, err := http.NewRequest("GET", url, nil)
func (r *Registry) Tag(originImage, newImage string) error {
original, err := r.parseReference(originImage)
if err != nil {
return nil, err
return err
}
req.SetBasicAuth(username, password)
req.Header.Set("Accept", "application/vnd.docker.distribution.manifest.v2+json")

resp, err := client.Do(req)
target, err := r.parseReference(newImage)
if err != nil {
return nil, err
}

if resp.StatusCode == http.StatusNotFound {
return nil, ErrorManifestNotFound
}

if resp.StatusCode != http.StatusOK {
return nil, errors.New(resp.Status)
return err
}

bodyText, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return bodyText, nil
}
originHost := r.wrapHost(target.Registry)
targetHost := r.wrapHost(original.Registry)

func (t *Client) pushManifest(username string, password string, hostName string, imageName string, tag string, manifest []byte) error {
var (
client = http.DefaultClient
url = "http://" + hostName + "/v2/" + imageName + "/manifests/" + tag
hub := registry.New(
registry.WithConfigHost(*originHost),
registry.WithConfigHost(*targetHost),
)
req, err := http.NewRequest("PUT", url, bytes.NewBuffer(manifest))
if err != nil {
return err
}
return hub.ImageCopy(context.Background(), original, target)
}

req.SetBasicAuth(username, password)
req.Header.Set("Content-type", "application/vnd.docker.distribution.manifest.v2+json")
func (r *Registry) wrapHost(hostUrl string) *config.Host {
host := config.HostNewDefName(nil, "http://"+hostUrl)
host.User = r.Username
host.Pass = r.Password
host.TLS = config.TLSDisabled
return host
}

resp, err := client.Do(req)
func (r *Registry) parseReference(image string) (ref.Ref, error) {
original, err := name.ParseReference(image)
if err != nil {
return err
return ref.Ref{}, err
}

if resp.StatusCode != http.StatusCreated {
return errors.New(resp.Status)
originalRef := ref.Ref{
Scheme: "reg",
Registry: original.Context().RegistryStr(),
Repository: original.Context().RepositoryStr(),
Tag: original.Identifier(),
}

return nil
return originalRef, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,8 @@ func TestClient_TagImage(t1 *testing.T) {
Password string
}
type args struct {
hostName string
imageName string
oldTag string
newTag string
originName string
newName string
}
tests := []struct {
name string
Expand All @@ -40,20 +38,18 @@ func TestClient_TagImage(t1 *testing.T) {
Password: "passw0rd",
},
args: args{
hostName: "sealos.hub:5000",
imageName: "default/devbox-sample",
oldTag: "2024-08-21-072021",
newTag: "test",
originName: "sealos.hub:5000/default/devbox-sample:2024-08-21-072021",
newName: "sealos.hub:5000/default/devbox-sample:test",
},
},
}
for _, tt := range tests {
t1.Run(tt.name, func(t1 *testing.T) {
t := &Client{
t := &Registry{
Username: tt.fields.Username,
Password: tt.fields.Password,
}
if err := t.TagImage(tt.args.hostName, tt.args.imageName, tt.args.oldTag, tt.args.newTag); (err != nil) != tt.wantErr {
if err := t.Tag(tt.args.originName, tt.args.newName); (err != nil) != tt.wantErr {
t1.Errorf("TagImage() error = %v, wantErr %v", err, tt.wantErr)
}
})
Expand Down

0 comments on commit a872a01

Please sign in to comment.