diff --git a/api/api/api_interface.go b/api/api/api_interface.go index cfa985250..5fcc8ac43 100644 --- a/api/api/api_interface.go +++ b/api/api/api_interface.go @@ -292,4 +292,5 @@ type RegistryAuthSecretInterface interface { type RegistryInterface interface { GetAllRepo(w http.ResponseWriter, r *http.Request) GetTagsByRepoName(w http.ResponseWriter, r *http.Request) + CheckRegistry(w http.ResponseWriter, r *http.Request) } diff --git a/api/api_routers/version2/v2Routers.go b/api/api_routers/version2/v2Routers.go index b4a61040d..968f77131 100644 --- a/api/api_routers/version2/v2Routers.go +++ b/api/api_routers/version2/v2Routers.go @@ -72,6 +72,7 @@ func (v2 *V2) proxyRoute() chi.Router { r := chi.NewRouter() r.Post("/registry/repos", controller.GetManager().GetAllRepo) r.Post("/registry/tags", controller.GetManager().GetTagsByRepoName) + r.Post("/registry/check", controller.GetManager().CheckRegistry) return r } diff --git a/api/controller/registry.go b/api/controller/registry.go index 36c1de02c..55165a422 100644 --- a/api/controller/registry.go +++ b/api/controller/registry.go @@ -1,21 +1,87 @@ package controller import ( - api_model "github.com/goodrain/rainbond/api/model" + "context" + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/remote" + "github.com/google/go-containerregistry/pkg/v1/remote/transport" + "strings" + "time" + + "crypto/tls" + apimodel "github.com/goodrain/rainbond/api/model" "github.com/goodrain/rainbond/api/util/bcode" "github.com/goodrain/rainbond/builder/sources/registry" httputil "github.com/goodrain/rainbond/util/http" "github.com/sirupsen/logrus" "net/http" + "net/url" ) // Registry - type Registry struct { } +// RepositoryTags - +type RepositoryTags struct { + Registry string `json:"registry"` + Repository string `json:"repository"` + Tags []string `json:"tags"` + Total int `json:"total"` +} + +// CheckRegistry 根据镜像仓库账号密码 检查镜像仓库是否可用 +func (r2 *Registry) CheckRegistry(w http.ResponseWriter, r *http.Request) { + var req apimodel.SearchByDomainRequest + ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &req, nil) + if !ok { + return + } + + if !strings.Contains(req.Domain, "://") { + req.Domain = "//" + req.Domain + } + + parse, err := url.Parse(req.Domain) + if err != nil { + logrus.Errorf("parse url error %s", err.Error()) + httputil.ReturnBcodeError(r, w, bcode.NewBadRequest(err.Error())) + return + } + + options := make([]name.Option, 0) + if parse.Scheme == "http" { + options = append(options, name.Insecure) + } + tr := http.DefaultTransport.(*http.Transport).Clone() + tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + + registryCfg, err := name.NewRegistry(parse.Host, options...) + if err != nil { + logrus.Errorf("parse registry error %s", err.Error()) + httputil.ReturnBcodeError(r, w, bcode.NewBadRequest(err.Error())) + return + } + ctx := context.Background() + ctx, cancel := context.WithTimeout(ctx, 3*time.Second) + defer cancel() + _, err = transport.NewWithContext(ctx, registryCfg, &authn.Basic{ + Username: req.UserName, + Password: req.Password, + }, tr, []string{}) + if err != nil { + logrus.Errorf("check registry error %s", err.Error()) + httputil.ReturnBcodeError(r, w, bcode.NewBadRequest(err.Error())) + return + } + + httputil.ReturnSuccess(r, w, true) +} + // GetAllRepo 根据镜像仓库账号密码 获取所有的镜像仓库 func (r2 *Registry) GetAllRepo(w http.ResponseWriter, r *http.Request) { - var req api_model.SearchByDomainRequest + var req apimodel.SearchByDomainRequest ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &req, nil) if !ok { return @@ -37,23 +103,39 @@ func (r2 *Registry) GetAllRepo(w http.ResponseWriter, r *http.Request) { // GetTagsByRepoName 根据镜像仓库账号密码 获取镜像tags func (r2 *Registry) GetTagsByRepoName(w http.ResponseWriter, r *http.Request) { - var req api_model.SearchByDomainRequest + var req apimodel.SearchByDomainRequest ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &req, nil) if !ok { return } - c, err := registry.NewInsecure(req.Domain, req.UserName, req.Password) + var regURL string + if strings.HasSuffix(req.Domain, "/") { + regURL = req.Domain + r.URL.Query().Get("repo") + } else { + regURL = req.Domain + "/" + r.URL.Query().Get("repo") + } + repo, err := name.NewRepository(regURL) if err != nil { + logrus.Errorf("parse registry error %s", err.Error()) httputil.ReturnBcodeError(r, w, bcode.NewBadRequest(err.Error())) - logrus.Errorf("get tags error %s", err.Error()) return } - tags, err := c.Tags(r.URL.Query().Get("repo")) + authenticator := authn.FromConfig(authn.AuthConfig{ + Username: req.UserName, + Password: req.Password, + }) + + tags, err := remote.List(repo, remote.WithAuth(authenticator)) if err != nil { - httputil.ReturnBcodeError(r, w, bcode.NewBadRequest(err.Error())) logrus.Errorf("get tags error %s", err.Error()) + httputil.ReturnBcodeError(r, w, bcode.NewBadRequest(err.Error())) return } - httputil.ReturnSuccess(r, w, tags) + httputil.ReturnSuccess(r, w, &RepositoryTags{ + Registry: repo.RegistryStr(), + Repository: repo.RepositoryStr(), + Tags: tags, + Total: len(tags), + }) } diff --git a/go.mod b/go.mod index 1975a59e1..62fc9e44a 100644 --- a/go.mod +++ b/go.mod @@ -111,6 +111,7 @@ require ( github.com/coreos/etcd v3.3.13+incompatible github.com/dustin/go-humanize v1.0.0 github.com/go-playground/assert/v2 v2.0.1 + github.com/google/go-containerregistry v0.5.1 github.com/helm/helm v2.17.0+incompatible golang.org/x/sync v0.2.0 k8s.io/apimachinery v0.28.3 diff --git a/go.sum b/go.sum index f98826b0a..88eb8b4f3 100644 --- a/go.sum +++ b/go.sum @@ -933,6 +933,7 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-containerregistry v0.5.1 h1:/+mFTs4AlwsJ/mJe8NDtKb7BxLtbZFpcn8vDsneEkwQ= github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=