From 117c36d52cab0a6614bf9a9cfb8150bb809c66b9 Mon Sep 17 00:00:00 2001 From: wang yan Date: Thu, 11 Apr 2019 23:13:51 +0800 Subject: [PATCH] Add api to get namespaces of registry To query the namespace of the registry according to its ID. Signed-off-by: wang yan --- docs/swagger.yaml | 45 +++++++++++++++- src/core/api/registry.go | 54 +++++++++++++++++++ src/core/router.go | 1 + src/replication/ng/adapter/harbor/adapter.go | 32 +++++++++-- .../ng/adapter/harbor/adapter_test.go | 29 +++++++++- 5 files changed, 154 insertions(+), 7 deletions(-) diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 8f05634e7..c4901b42e 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -2291,6 +2291,39 @@ paths: description: Registry not found '500': description: Unexpected internal errors. + /registries/{id}/namespace: + get: + summary: List namespaces of registry + description: | + This endpoint let user list namespaces of registry according to query. + parameters: + - name: id + in: query + type: integer + required: true + description: The registry ID. + - name: name + in: query + type: string + required: false + description: The name of namespace. + tags: + - Products + responses: + '200': + description: Success + schema: + type: array + items: + $ref: '#/definitions/Namespace' + '401': + description: User need to login first. + '404': + description: No registry found. + '403': + description: User has no privilege for the operation. + '500': + description: Unexpected internal errors. /internal/syncregistry: post: summary: Sync repositories from registry to DB. @@ -4965,4 +4998,14 @@ definitions: description: The start time end_time: type: string - description: The end time \ No newline at end of file + description: The end time + Namespace: + type: object + description: The namespace of registry + properties: + name: + type: string + description: The name of namespace + metadata: + type: object + description: The metadata of namespace \ No newline at end of file diff --git a/src/core/api/registry.go b/src/core/api/registry.go index 2fa1a174f..a8b2eb974 100644 --- a/src/core/api/registry.go +++ b/src/core/api/registry.go @@ -321,6 +321,60 @@ func (t *RegistryAPI) GetInfo() { t.WriteJSONData(process(info)) } +// GetNamespace get the namespace of a registry +func (t *RegistryAPI) GetNamespace() { + var registry *model.Registry + var err error + + id, err := t.GetInt64FromPath(":id") + if err != nil || id < 0 { + t.HandleBadRequest(fmt.Sprintf("invalid registry ID %s", t.GetString(":id"))) + return + } + if id > 0 { + registry, err = t.manager.Get(id) + if err != nil { + t.HandleInternalServerError(fmt.Sprintf("failed to get registry %d: %v", id, err)) + return + } + } else if id == 0 { + registry = event.GetLocalRegistry() + } + + if registry == nil { + t.HandleNotFound(fmt.Sprintf("registry %d not found", id)) + return + } + + if !adapter.HasFactory(registry.Type) { + t.HandleInternalServerError(fmt.Sprintf("no adapter factory found for %s", registry.Type)) + return + } + + regFactory, err := adapter.GetFactory(registry.Type) + if err != nil { + t.HandleInternalServerError(fmt.Sprintf("fail to get adapter factory %s", registry.Type)) + return + } + regAdapter, err := regFactory(registry) + if err != nil { + t.HandleInternalServerError(fmt.Sprintf("fail to get adapter %s", registry.Type)) + return + } + + query := &model.NamespaceQuery{ + Name: t.GetString("name"), + } + npResults, err := regAdapter.ListNamespaces(query) + if err != nil { + t.HandleInternalServerError(fmt.Sprintf("fail to list namespaces %s %v", registry.Type, err)) + return + } + + t.Data["json"] = npResults + t.ServeJSON() +} + // merge "SupportedResourceTypes" into "SupportedResourceFilters" for UI to render easier func process(info *model.RegistryInfo) *model.RegistryInfo { if info == nil { diff --git a/src/core/router.go b/src/core/router.go index cb55b7403..ed3630200 100644 --- a/src/core/router.go +++ b/src/core/router.go @@ -135,6 +135,7 @@ func initRouters() { beego.Router("/api/registries/ping", &api.RegistryAPI{}, "post:Ping") // we use "0" as the ID of the local Harbor registry, so don't add "([0-9]+)" in the path beego.Router("/api/registries/:id/info", &api.RegistryAPI{}, "get:GetInfo") + beego.Router("/api/registries/:id/namespace", &api.RegistryAPI{}, "get:GetNamespace") beego.Router("/v2/*", &controllers.RegistryProxy{}, "*:Handle") diff --git a/src/replication/ng/adapter/harbor/adapter.go b/src/replication/ng/adapter/harbor/adapter.go index 3a6eb049e..1a4454340 100644 --- a/src/replication/ng/adapter/harbor/adapter.go +++ b/src/replication/ng/adapter/harbor/adapter.go @@ -125,10 +125,24 @@ func (a *adapter) Info() (*model.RegistryInfo, error) { return info, nil } -// TODO implement the function -func (a *adapter) ListNamespaces(*model.NamespaceQuery) ([]*model.Namespace, error) { - return nil, nil +func (a *adapter) ListNamespaces(npQuery *model.NamespaceQuery) ([]*model.Namespace, error) { + var nps []*model.Namespace + projects, err := a.getProjects(npQuery.Name) + if err != nil { + return nil, err + } + + for _, pro := range projects { + nps = append(nps, &model.Namespace{ + Name: pro.Name, + Metadata: pro.Metadata, + }) + } + + return nps, nil + } + func (a *adapter) ConvertResourceMetadata(metadata *model.ResourceMetadata, namespace *model.Namespace) (*model.ResourceMetadata, error) { if metadata == nil { return nil, errors.New("the metadata cannot be null") @@ -237,13 +251,21 @@ type project struct { Metadata map[string]interface{} `json:"metadata"` } -func (a *adapter) getProject(name string) (*project, error) { - // TODO need an API to exact match project by name +func (a *adapter) getProjects(name string) ([]*project, error) { projects := []*project{} url := fmt.Sprintf("%s/api/projects?name=%s&page=1&page_size=1000", a.coreServiceURL, name) if err := a.client.Get(url, &projects); err != nil { return nil, err } + return projects, nil +} + +func (a *adapter) getProject(name string) (*project, error) { + // TODO need an API to exact match project by name + projects, err := a.getProjects(name) + if err != nil { + return nil, err + } for _, pro := range projects { if pro.Name == name { diff --git a/src/replication/ng/adapter/harbor/adapter_test.go b/src/replication/ng/adapter/harbor/adapter_test.go index 4efa098ca..c9ca2a068 100644 --- a/src/replication/ng/adapter/harbor/adapter_test.go +++ b/src/replication/ng/adapter/harbor/adapter_test.go @@ -72,7 +72,34 @@ func TestInfo(t *testing.T) { } func TestListNamespaces(t *testing.T) { - // TODO + // project exists + server := test.NewServer(&test.RequestHandlerMapping{ + Method: http.MethodGet, + Pattern: "/api/projects", + Handler: func(w http.ResponseWriter, r *http.Request) { + data := `[{ + "name": "library", + "metadata": {"public":true} + },{ + "name": "library1", + "metadata": {"public":true} + }]` + w.Write([]byte(data)) + }, + }) + defer server.Close() + registry := &model.Registry{ + URL: server.URL, + } + adapter := newAdapter(registry) + npQuery := &model.NamespaceQuery{ + Name: "lib", + } + namespace, err := adapter.ListNamespaces(npQuery) + require.Nil(t, err) + assert.Equal(t, 2, len(namespace)) + assert.Equal(t, "library", namespace[0].Name) + assert.True(t, namespace[0].Metadata["public"].(bool)) } func TestPrepareForPush(t *testing.T) {