mirror of https://github.com/portainer/portainer
feat(registry): Add ProGet registry type EE-703 (#5196)
* intermediate commit * feat(registry): backport ProGet registry to CE (#954) * backport EE changes * label updates and remove auth-toggle Co-authored-by: Dennis Buduev <dennis.buduev@portainer.io>pull/5266/head
parent
8b80eb1731
commit
90a472c08b
|
@ -5,7 +5,7 @@ import (
|
|||
|
||||
"github.com/gorilla/mux"
|
||||
httperror "github.com/portainer/libhttp/error"
|
||||
"github.com/portainer/portainer/api"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/http/proxy"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
)
|
||||
|
@ -18,19 +18,28 @@ func hideFields(registry *portainer.Registry) {
|
|||
// Handler is the HTTP handler used to handle registry operations.
|
||||
type Handler struct {
|
||||
*mux.Router
|
||||
requestBouncer *security.RequestBouncer
|
||||
DataStore portainer.DataStore
|
||||
FileService portainer.FileService
|
||||
ProxyManager *proxy.Manager
|
||||
requestBouncer *security.RequestBouncer
|
||||
DataStore portainer.DataStore
|
||||
FileService portainer.FileService
|
||||
ProxyManager *proxy.Manager
|
||||
}
|
||||
|
||||
// NewHandler creates a handler to manage registry operations.
|
||||
func NewHandler(bouncer *security.RequestBouncer) *Handler {
|
||||
h := &Handler{
|
||||
Router: mux.NewRouter(),
|
||||
requestBouncer: bouncer,
|
||||
}
|
||||
h := newHandler(bouncer)
|
||||
h.initRouter(bouncer)
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func newHandler(bouncer *security.RequestBouncer) *Handler {
|
||||
return &Handler{
|
||||
Router: mux.NewRouter(),
|
||||
requestBouncer: bouncer,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) initRouter(bouncer accessGuard) {
|
||||
h.Handle("/registries",
|
||||
bouncer.AdminAccess(httperror.LoggerHandler(h.registryCreate))).Methods(http.MethodPost)
|
||||
h.Handle("/registries",
|
||||
|
@ -45,5 +54,10 @@ func NewHandler(bouncer *security.RequestBouncer) *Handler {
|
|||
bouncer.AdminAccess(httperror.LoggerHandler(h.registryDelete))).Methods(http.MethodDelete)
|
||||
h.PathPrefix("/registries/proxies/gitlab").Handler(
|
||||
bouncer.AdminAccess(httperror.LoggerHandler(h.proxyRequestsToGitlabAPIWithoutRegistry)))
|
||||
return h
|
||||
}
|
||||
|
||||
type accessGuard interface {
|
||||
AdminAccess(h http.Handler) http.Handler
|
||||
RestrictedAccess(h http.Handler) http.Handler
|
||||
AuthenticatedAccess(h http.Handler) http.Handler
|
||||
}
|
|
@ -2,6 +2,7 @@ package registries
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/asaskevich/govalidator"
|
||||
|
@ -14,10 +15,12 @@ import (
|
|||
type registryCreatePayload struct {
|
||||
// Name that will be used to identify this registry
|
||||
Name string `example:"my-registry" validate:"required"`
|
||||
// Registry Type. Valid values are: 1 (Quay.io), 2 (Azure container registry), 3 (custom registry) or 4 (Gitlab registry)
|
||||
Type portainer.RegistryType `example:"1" validate:"required" enums:"1,2,3,4"`
|
||||
// Registry Type. Valid values are: 1 (Quay.io), 2 (Azure container registry), 3 (custom registry), 4 (Gitlab registry) or 5 (ProGet registry)
|
||||
Type portainer.RegistryType `example:"1" validate:"required" enums:"1,2,3,4,5"`
|
||||
// URL or IP address of the Docker registry
|
||||
URL string `example:"registry.mydomain.tld:2375" validate:"required"`
|
||||
URL string `example:"registry.mydomain.tld:2375/feed" validate:"required"`
|
||||
// BaseURL required for ProGet registry
|
||||
BaseURL string `example:"registry.mydomain.tld:2375"`
|
||||
// Is authentication against this registry enabled
|
||||
Authentication bool `example:"false" validate:"required"`
|
||||
// Username used to authenticate against this registry. Required when Authentication is true
|
||||
|
@ -30,7 +33,7 @@ type registryCreatePayload struct {
|
|||
Quay portainer.QuayRegistryData
|
||||
}
|
||||
|
||||
func (payload *registryCreatePayload) Validate(r *http.Request) error {
|
||||
func (payload *registryCreatePayload) Validate(_ *http.Request) error {
|
||||
if govalidator.IsNull(payload.Name) {
|
||||
return errors.New("Invalid registry name")
|
||||
}
|
||||
|
@ -40,9 +43,17 @@ func (payload *registryCreatePayload) Validate(r *http.Request) error {
|
|||
if payload.Authentication && (govalidator.IsNull(payload.Username) || govalidator.IsNull(payload.Password)) {
|
||||
return errors.New("Invalid credentials. Username and password must be specified when authentication is enabled")
|
||||
}
|
||||
if payload.Type != portainer.QuayRegistry && payload.Type != portainer.AzureRegistry && payload.Type != portainer.CustomRegistry && payload.Type != portainer.GitlabRegistry {
|
||||
return errors.New("Invalid registry type. Valid values are: 1 (Quay.io), 2 (Azure container registry), 3 (custom registry) or 4 (Gitlab registry)")
|
||||
|
||||
switch payload.Type {
|
||||
case portainer.QuayRegistry, portainer.AzureRegistry, portainer.CustomRegistry, portainer.GitlabRegistry, portainer.ProGetRegistry:
|
||||
default:
|
||||
return errors.New("invalid registry type. Valid values are: 1 (Quay.io), 2 (Azure container registry), 3 (custom registry), 4 (Gitlab registry) or 5 (ProGet registry)")
|
||||
}
|
||||
|
||||
if payload.Type == portainer.ProGetRegistry && payload.BaseURL == "" {
|
||||
return fmt.Errorf("BaseURL is required for registry type %d (ProGet)", portainer.ProGetRegistry)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -70,6 +81,7 @@ func (handler *Handler) registryCreate(w http.ResponseWriter, r *http.Request) *
|
|||
Type: portainer.RegistryType(payload.Type),
|
||||
Name: payload.Name,
|
||||
URL: payload.URL,
|
||||
BaseURL: payload.BaseURL,
|
||||
Authentication: payload.Authentication,
|
||||
Username: payload.Username,
|
||||
Password: payload.Password,
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
package registries
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_registryCreatePayload_Validate(t *testing.T) {
|
||||
basePayload := registryCreatePayload{Name: "Test registry", URL: "http://example.com"}
|
||||
t.Run("Can't create a ProGet registry if BaseURL is empty", func(t *testing.T) {
|
||||
payload := basePayload
|
||||
payload.Type = portainer.ProGetRegistry
|
||||
err := payload.Validate(nil)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("Can create a GitLab registry if BaseURL is empty", func(t *testing.T) {
|
||||
payload := basePayload
|
||||
payload.Type = portainer.GitlabRegistry
|
||||
err := payload.Validate(nil)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("Can create a ProGet registry if BaseURL is not empty", func(t *testing.T) {
|
||||
payload := basePayload
|
||||
payload.Type = portainer.ProGetRegistry
|
||||
payload.BaseURL = "http://example.com"
|
||||
err := payload.Validate(nil)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
type testRegistryService struct {
|
||||
portainer.RegistryService
|
||||
createRegistry func(r *portainer.Registry) error
|
||||
updateRegistry func(ID portainer.RegistryID, r *portainer.Registry) error
|
||||
getRegistry func(ID portainer.RegistryID) (*portainer.Registry, error)
|
||||
}
|
||||
|
||||
type testDataStore struct {
|
||||
portainer.DataStore
|
||||
registry *testRegistryService
|
||||
}
|
||||
|
||||
func (t testDataStore) Registry() portainer.RegistryService {
|
||||
return t.registry
|
||||
}
|
||||
|
||||
func (t testRegistryService) CreateRegistry(r *portainer.Registry) error {
|
||||
return t.createRegistry(r)
|
||||
}
|
||||
|
||||
func (t testRegistryService) UpdateRegistry(ID portainer.RegistryID, r *portainer.Registry) error {
|
||||
return t.updateRegistry(ID, r)
|
||||
}
|
||||
|
||||
func (t testRegistryService) Registry(ID portainer.RegistryID) (*portainer.Registry, error) {
|
||||
return t.getRegistry(ID)
|
||||
}
|
||||
|
||||
func (t testRegistryService) Registries() ([]portainer.Registry, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func TestHandler_registryCreate(t *testing.T) {
|
||||
payload := registryCreatePayload{
|
||||
Name: "Test registry",
|
||||
Type: portainer.ProGetRegistry,
|
||||
URL: "http://example.com",
|
||||
BaseURL: "http://example.com",
|
||||
Authentication: false,
|
||||
Username: "username",
|
||||
Password: "password",
|
||||
Gitlab: portainer.GitlabRegistryData{},
|
||||
}
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
assert.NoError(t, err)
|
||||
r := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(payloadBytes))
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
registry := portainer.Registry{}
|
||||
handler := Handler{}
|
||||
handler.DataStore = testDataStore{
|
||||
registry: &testRegistryService{
|
||||
createRegistry: func(r *portainer.Registry) error {
|
||||
registry = *r
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
handlerError := handler.registryCreate(w, r)
|
||||
assert.Nil(t, handlerError)
|
||||
assert.Equal(t, payload.Name, registry.Name)
|
||||
assert.Equal(t, payload.Type, registry.Type)
|
||||
assert.Equal(t, payload.URL, registry.URL)
|
||||
assert.Equal(t, payload.BaseURL, registry.BaseURL)
|
||||
assert.Equal(t, payload.Authentication, registry.Authentication)
|
||||
assert.Equal(t, payload.Username, registry.Username)
|
||||
assert.Equal(t, payload.Password, registry.Password)
|
||||
}
|
|
@ -12,18 +12,14 @@ import (
|
|||
)
|
||||
|
||||
type registryUpdatePayload struct {
|
||||
// Name that will be used to identify this registry
|
||||
Name *string `validate:"required" example:"my-registry"`
|
||||
// URL or IP address of the Docker registry
|
||||
URL *string `validate:"required" example:"registry.mydomain.tld:2375"`
|
||||
// Is authentication against this registry enabled
|
||||
Authentication *bool `example:"false" validate:"required"`
|
||||
// Username used to authenticate against this registry. Required when Authentication is true
|
||||
Username *string `example:"registry_user"`
|
||||
// Password used to authenticate against this registry. required when Authentication is true
|
||||
Password *string `example:"registry_password"`
|
||||
UserAccessPolicies portainer.UserAccessPolicies
|
||||
TeamAccessPolicies portainer.TeamAccessPolicies
|
||||
Name *string `json:",omitempty" example:"my-registry" validate:"required"`
|
||||
URL *string `json:",omitempty" example:"registry.mydomain.tld:2375/feed" validate:"required"`
|
||||
BaseURL *string `json:",omitempty" example:"registry.mydomain.tld:2375"`
|
||||
Authentication *bool `json:",omitempty" example:"false" validate:"required"`
|
||||
Username *string `json:",omitempty" example:"registry_user"`
|
||||
Password *string `json:",omitempty" example:"registry_password"`
|
||||
UserAccessPolicies portainer.UserAccessPolicies `json:",omitempty"`
|
||||
TeamAccessPolicies portainer.TeamAccessPolicies `json:",omitempty"`
|
||||
Quay *portainer.QuayRegistryData
|
||||
}
|
||||
|
||||
|
@ -84,6 +80,10 @@ func (handler *Handler) registryUpdate(w http.ResponseWriter, r *http.Request) *
|
|||
registry.URL = *payload.URL
|
||||
}
|
||||
|
||||
if registry.Type == portainer.ProGetRegistry && payload.BaseURL != nil {
|
||||
registry.BaseURL = *payload.BaseURL
|
||||
}
|
||||
|
||||
if payload.Authentication != nil {
|
||||
if *payload.Authentication {
|
||||
registry.Authentication = true
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
package registries
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func ps(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func pb(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
|
||||
type TestBouncer struct{}
|
||||
|
||||
func (t TestBouncer) AdminAccess(h http.Handler) http.Handler {
|
||||
return h
|
||||
}
|
||||
|
||||
func (t TestBouncer) RestrictedAccess(h http.Handler) http.Handler {
|
||||
return h
|
||||
}
|
||||
|
||||
func (t TestBouncer) AuthenticatedAccess(h http.Handler) http.Handler {
|
||||
return h
|
||||
}
|
||||
|
||||
func TestHandler_registryUpdate(t *testing.T) {
|
||||
payload := registryUpdatePayload{
|
||||
Name: ps("Updated test registry"),
|
||||
URL: ps("http://example.org/feed"),
|
||||
BaseURL: ps("http://example.org"),
|
||||
Authentication: pb(true),
|
||||
Username: ps("username"),
|
||||
Password: ps("password"),
|
||||
}
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
assert.NoError(t, err)
|
||||
registry := portainer.Registry{Type: portainer.ProGetRegistry, ID: 5}
|
||||
r := httptest.NewRequest(http.MethodPut, "/registries/5", bytes.NewReader(payloadBytes))
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
updatedRegistry := portainer.Registry{}
|
||||
handler := newHandler(nil)
|
||||
handler.initRouter(TestBouncer{})
|
||||
handler.DataStore = testDataStore{
|
||||
registry: &testRegistryService{
|
||||
getRegistry: func(_ portainer.RegistryID) (*portainer.Registry, error) {
|
||||
return ®istry, nil
|
||||
},
|
||||
updateRegistry: func(ID portainer.RegistryID, r *portainer.Registry) error {
|
||||
assert.Equal(t, ID, r.ID)
|
||||
updatedRegistry = *r
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
handler.Router.ServeHTTP(w, r)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
// Registry type should remain intact
|
||||
assert.Equal(t, registry.Type, updatedRegistry.Type)
|
||||
|
||||
assert.Equal(t, *payload.Name, updatedRegistry.Name)
|
||||
assert.Equal(t, *payload.URL, updatedRegistry.URL)
|
||||
assert.Equal(t, *payload.BaseURL, updatedRegistry.BaseURL)
|
||||
assert.Equal(t, *payload.Authentication, updatedRegistry.Authentication)
|
||||
assert.Equal(t, *payload.Username, updatedRegistry.Username)
|
||||
assert.Equal(t, *payload.Password, updatedRegistry.Password)
|
||||
|
||||
}
|
|
@ -511,12 +511,14 @@ type (
|
|||
Registry struct {
|
||||
// Registry Identifier
|
||||
ID RegistryID `json:"Id" example:"1"`
|
||||
// Registry Type (1 - Quay, 2 - Azure, 3 - Custom, 4 - Gitlab)
|
||||
Type RegistryType `json:"Type" enums:"1,2,3,4"`
|
||||
// Registry Type (1 - Quay, 2 - Azure, 3 - Custom, 4 - Gitlab, 5 - ProGet)
|
||||
Type RegistryType `json:"Type" enums:"1,2,3,4,5"`
|
||||
// Registry Name
|
||||
Name string `json:"Name" example:"my-registry"`
|
||||
// URL or IP address of the Docker registry
|
||||
URL string `json:"URL" example:"registry.mydomain.tld:2375"`
|
||||
// Base URL, introduced for ProGet registry
|
||||
BaseURL string `json:"BaseURL" example:"registry.mydomain.tld:2375"`
|
||||
// Is authentication against this registry enabled
|
||||
Authentication bool `json:"Authentication" example:"true"`
|
||||
// Username used to authenticate against this registry
|
||||
|
@ -1489,6 +1491,8 @@ const (
|
|||
CustomRegistry
|
||||
// GitlabRegistry represents a gitlab registry
|
||||
GitlabRegistry
|
||||
// ProGetRegistry represents a proget registry
|
||||
ProGetRegistry
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -1187,17 +1187,22 @@ definitions:
|
|||
TeamAccessPolicies:
|
||||
$ref: '#/definitions/portainer.TeamAccessPolicies'
|
||||
Type:
|
||||
description: Registry Type (1 - Quay, 2 - Azure, 3 - Custom, 4 - Gitlab)
|
||||
description: Registry Type (1 - Quay, 2 - Azure, 3 - Custom, 4 - Gitlab, 5 - ProGet)
|
||||
enum:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 4
|
||||
- 5
|
||||
type: integer
|
||||
URL:
|
||||
description: URL or IP address of the Docker registry
|
||||
example: registry.mydomain.tld:2375
|
||||
type: string
|
||||
BaseURL:
|
||||
description: Base URL or IP address of the ProGet registry
|
||||
example: registry.mydomain.tld:2375
|
||||
type: string
|
||||
UserAccessPolicies:
|
||||
$ref: '#/definitions/portainer.UserAccessPolicies'
|
||||
Username:
|
||||
|
@ -1827,18 +1832,23 @@ definitions:
|
|||
type: string
|
||||
type:
|
||||
description: 'Registry Type. Valid values are: 1 (Quay.io), 2 (Azure container
|
||||
registry), 3 (custom registry) or 4 (Gitlab registry)'
|
||||
registry), 3 (custom registry), 4 (Gitlab registry) or 5 (ProGet registry)'
|
||||
enum:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 4
|
||||
- 5
|
||||
example: 1
|
||||
type: integer
|
||||
url:
|
||||
description: URL or IP address of the Docker registry
|
||||
example: registry.mydomain.tld:2375
|
||||
type: string
|
||||
baseUrl:
|
||||
description: Base URL or IP address of the ProGet registry
|
||||
example: registry.mydomain.tld:2375
|
||||
type: string
|
||||
username:
|
||||
description: Username used to authenticate against this registry. Required
|
||||
when Authentication is true
|
||||
|
@ -1871,6 +1881,10 @@ definitions:
|
|||
description: URL or IP address of the Docker registry
|
||||
example: registry.mydomain.tld:2375
|
||||
type: string
|
||||
baseUrl:
|
||||
description: Base URL or IP address of the ProGet registry
|
||||
example: registry.mydomain.tld:2375
|
||||
type: string
|
||||
userAccessPolicies:
|
||||
$ref: '#/definitions/portainer.UserAccessPolicies'
|
||||
username:
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
<form class="form-horizontal" name="registryFormProGet" ng-submit="$ctrl.formAction()">
|
||||
<div class="col-sm-12 form-section-title">
|
||||
ProGet registry details
|
||||
</div>
|
||||
<!-- name-input -->
|
||||
<div class="form-group">
|
||||
<label for="registry_name" class="col-sm-3 col-lg-2 control-label text-left">Name</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="text" class="form-control" id="registry_name" name="registry_name" ng-model="$ctrl.model.Name" placeholder="proget-registry" required auto-focus />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="registryFormProGet.registry_name.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="registryFormProGet.registry_name.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i>This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !name-input -->
|
||||
<!-- url-input -->
|
||||
<div class="form-group">
|
||||
<label for="registry_url" class="col-sm-3 col-lg-2 control-label text-left">
|
||||
Registry URL
|
||||
<portainer-tooltip position="bottom" message="The URL of the ProGet registry including the Feed name"></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="text" class="form-control" id="registry_url" name="registry_url" ng-model="$ctrl.model.URL" placeholder="proget.example.com/example-registry" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="registryFormProGet.registry_url.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="registryFormProGet.registry_url.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- url-input -->
|
||||
<!-- base-url-input -->
|
||||
<div class="form-group">
|
||||
<label for="registry_base_url" class="col-sm-3 col-lg-2 control-label text-left">
|
||||
Base URL
|
||||
<portainer-tooltip position="bottom" message="The base URL of the ProGet registry"></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="text" class="form-control" id="registry_base_url" name="registry_base_url" ng-model="$ctrl.model.BaseURL" placeholder="proget.example.com" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="registryFormProGet.registry_base_url.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="registryFormProGet.registry_base_url.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !base-url-input -->
|
||||
<div>
|
||||
<!-- credentials-user -->
|
||||
<div class="form-group">
|
||||
<label for="registry_username" class="col-sm-3 col-lg-2 control-label text-left">Username</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="text" class="form-control" id="registry_username" name="registry_username" ng-model="$ctrl.model.Username" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="registryFormProGet.registry_username.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="registryFormProGet.registry_username.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !credentials-user -->
|
||||
<!-- credentials-password -->
|
||||
<div class="form-group">
|
||||
<label for="registry_password" class="col-sm-3 col-lg-2 control-label text-left">Password</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="password" class="form-control" id="registry_password" name="registry_password" ng-model="$ctrl.model.Password" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="registryFormProGet.registry_password.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="registryFormProGet.registry_password.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !credentials-password -->
|
||||
</div>
|
||||
<!-- actions -->
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Actions
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="$ctrl.actionInProgress || !registryFormProGet.$valid" button-spinner="$ctrl.actionInProgress">
|
||||
<span ng-hide="$ctrl.actionInProgress">{{ $ctrl.formActionLabel }}</span>
|
||||
<span ng-show="$ctrl.actionInProgress">In progress...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !actions -->
|
||||
</form>
|
|
@ -0,0 +1,9 @@
|
|||
angular.module('portainer.app').component('registryFormProget', {
|
||||
templateUrl: './registry-form-proget.html',
|
||||
bindings: {
|
||||
model: '=',
|
||||
formAction: '<',
|
||||
formActionLabel: '@',
|
||||
actionInProgress: '<',
|
||||
},
|
||||
});
|
|
@ -1,11 +1,12 @@
|
|||
import _ from 'lodash-es';
|
||||
import { RegistryTypes } from '@/portainer/models/registryTypes';
|
||||
import { RegistryTypes } from './registryTypes';
|
||||
|
||||
export function RegistryViewModel(data) {
|
||||
this.Id = data.Id;
|
||||
this.Type = data.Type;
|
||||
this.Name = data.Name;
|
||||
this.URL = data.URL;
|
||||
this.BaseURL = data.BaseURL;
|
||||
this.Authentication = data.Authentication;
|
||||
this.Username = data.Username;
|
||||
this.Password = data.Password;
|
||||
|
@ -33,7 +34,7 @@ export function RegistryManagementConfigurationDefaultModel(registry) {
|
|||
this.TLS = true;
|
||||
}
|
||||
|
||||
if (registry.Type === RegistryTypes.CUSTOM && registry.Authentication) {
|
||||
if ((registry.Type === RegistryTypes.CUSTOM || registry.Type === RegistryTypes.PROGET) && registry.Authentication) {
|
||||
this.Authentication = true;
|
||||
this.Username = registry.Username;
|
||||
}
|
||||
|
@ -71,4 +72,8 @@ export function RegistryCreateRequest(model) {
|
|||
organisationName: model.Quay.organisationName,
|
||||
};
|
||||
}
|
||||
if (model.Type === RegistryTypes.PROGET) {
|
||||
this.BaseURL = _.replace(model.BaseURL, /^https?\:\/\//i, '');
|
||||
this.BaseURL = _.replace(this.BaseURL, /\/$/, '');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,4 +3,5 @@ export const RegistryTypes = Object.freeze({
|
|||
AZURE: 2,
|
||||
CUSTOM: 3,
|
||||
GITLAB: 4,
|
||||
PROGET: 5,
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { RegistryTypes } from '@/portainer/models/registryTypes';
|
||||
import { RegistryDefaultModel } from '../../../models/registry';
|
||||
import { RegistryDefaultModel } from '@/portainer/models/registry';
|
||||
|
||||
angular.module('portainer.app').controller('CreateRegistryController', [
|
||||
'$scope',
|
||||
|
@ -11,6 +11,7 @@ angular.module('portainer.app').controller('CreateRegistryController', [
|
|||
$scope.selectQuayRegistry = selectQuayRegistry;
|
||||
$scope.selectAzureRegistry = selectAzureRegistry;
|
||||
$scope.selectCustomRegistry = selectCustomRegistry;
|
||||
$scope.selectProGetRegistry = selectProGetRegistry;
|
||||
$scope.selectGitlabRegistry = selectGitlabRegistry;
|
||||
$scope.create = createRegistry;
|
||||
$scope.useDefaultGitlabConfiguration = useDefaultGitlabConfiguration;
|
||||
|
@ -65,6 +66,13 @@ angular.module('portainer.app').controller('CreateRegistryController', [
|
|||
$scope.model.Authentication = false;
|
||||
}
|
||||
|
||||
function selectProGetRegistry() {
|
||||
$scope.model.Name = '';
|
||||
$scope.model.URL = '';
|
||||
$scope.model.BaseURL = '';
|
||||
$scope.model.Authentication = true;
|
||||
}
|
||||
|
||||
function retrieveGitlabRegistries() {
|
||||
$scope.state.actionInProgress = true;
|
||||
RegistryGitlabService.projects($scope.model.Gitlab.InstanceURL, $scope.model.Token)
|
||||
|
|
|
@ -26,6 +26,16 @@
|
|||
<p>Quay container registry</p>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" id="registry_proget" ng-model="model.Type" ng-value="RegistryTypes.PROGET" />
|
||||
<label for="registry_proget" ng-click="selectProGetRegistry()">
|
||||
<div class="boxselector_header">
|
||||
<i class="fa fa-database" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
ProGet
|
||||
</div>
|
||||
<p>ProGet container registry</p>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" id="registry_azure" ng-model="model.Type" ng-value="RegistryTypes.AZURE" />
|
||||
<label for="registry_azure" ng-click="selectAzureRegistry()">
|
||||
|
@ -83,6 +93,14 @@
|
|||
action-in-progress="state.actionInProgress"
|
||||
></registry-form-custom>
|
||||
|
||||
<registry-form-proget
|
||||
ng-if="model.Type === RegistryTypes.PROGET"
|
||||
model="model"
|
||||
form-action="create"
|
||||
form-action-label="Add registry"
|
||||
action-in-progress="state.actionInProgress"
|
||||
></registry-form-proget>
|
||||
|
||||
<registry-form-gitlab
|
||||
ng-if="model.Type === RegistryTypes.GITLAB"
|
||||
model="model"
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
</div>
|
||||
<!-- !registry-url-input -->
|
||||
<!-- authentication-checkbox -->
|
||||
<div class="form-group">
|
||||
<div class="form-group" ng-if="registry.Type !== RegistryTypes.PROGET">
|
||||
<div class="col-sm-12">
|
||||
<label for="registry_auth" class="control-label text-left">
|
||||
Authentication
|
||||
|
|
Loading…
Reference in New Issue