2018-06-11 15:13:19 +02:00
package registries
import (
2020-07-08 00:57:52 +03:00
"errors"
2021-07-01 14:57:15 +12:00
"fmt"
2018-06-11 15:13:19 +02:00
"net/http"
2021-02-23 05:21:39 +02:00
portainer "github.com/portainer/portainer/api"
2021-07-14 11:15:21 +02:00
httperrors "github.com/portainer/portainer/api/http/errors"
"github.com/portainer/portainer/api/http/security"
2023-09-01 19:27:02 -03:00
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
2018-06-11 15:13:19 +02:00
)
type registryCreatePayload struct {
2021-02-23 05:21:39 +02:00
// Name that will be used to identify this registry
Name string ` example:"my-registry" validate:"required" `
2021-12-01 13:18:57 +13:00
// Registry Type. Valid values are:
// 1 (Quay.io),
// 2 (Azure container registry),
// 3 (custom registry),
// 4 (Gitlab registry),
// 5 (ProGet registry),
// 6 (DockerHub)
// 7 (ECR)
Type portainer . RegistryType ` example:"1" validate:"required" enums:"1,2,3,4,5,6,7" `
2021-02-23 05:21:39 +02:00
// URL or IP address of the Docker registry
2021-07-01 14:57:15 +12:00
URL string ` example:"registry.mydomain.tld:2375/feed" validate:"required" `
// BaseURL required for ProGet registry
BaseURL string ` example:"registry.mydomain.tld:2375" `
2021-02-23 05:21:39 +02:00
// 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" `
// Gitlab specific details, required when type = 4
Gitlab portainer . GitlabRegistryData
2021-03-13 12:47:35 +13:00
// Quay specific details, required when type = 1
Quay portainer . QuayRegistryData
2021-12-01 13:18:57 +13:00
// ECR specific details, required when type = 7
Ecr portainer . EcrData
2018-06-11 15:13:19 +02:00
}
2021-07-01 14:57:15 +12:00
func ( payload * registryCreatePayload ) Validate ( _ * http . Request ) error {
2024-08-28 19:37:20 -03:00
if len ( payload . Name ) == 0 {
2020-07-08 00:57:52 +03:00
return errors . New ( "Invalid registry name" )
2018-06-11 15:13:19 +02:00
}
2024-08-28 19:37:20 -03:00
if len ( payload . URL ) == 0 {
2020-07-08 00:57:52 +03:00
return errors . New ( "Invalid registry URL" )
2018-06-11 15:13:19 +02:00
}
2021-12-01 13:18:57 +13:00
if payload . Authentication {
2024-08-28 19:37:20 -03:00
if len ( payload . Username ) == 0 || len ( payload . Password ) == 0 {
2021-12-01 13:18:57 +13:00
return errors . New ( "Invalid credentials. Username and password must be specified when authentication is enabled" )
}
if payload . Type == portainer . EcrRegistry {
2024-08-28 19:37:20 -03:00
if len ( payload . Ecr . Region ) == 0 {
2021-12-01 13:18:57 +13:00
return errors . New ( "invalid credentials: access key ID, secret access key and region must be specified when authentication is enabled" )
}
}
2018-06-11 15:13:19 +02:00
}
2021-07-01 14:57:15 +12:00
switch payload . Type {
2021-12-01 13:18:57 +13:00
case portainer . QuayRegistry , portainer . AzureRegistry , portainer . CustomRegistry , portainer . GitlabRegistry , portainer . ProGetRegistry , portainer . DockerHubRegistry , portainer . EcrRegistry :
2021-07-01 14:57:15 +12:00
default :
2021-12-01 13:18:57 +13:00
return errors . New ( "invalid registry type. Valid values are: 1 (Quay.io), 2 (Azure container registry), 3 (custom registry), 4 (Gitlab registry), 5 (ProGet registry), 6 (DockerHub), 7 (ECR)" )
2021-07-01 14:57:15 +12:00
}
if payload . Type == portainer . ProGetRegistry && payload . BaseURL == "" {
return fmt . Errorf ( "BaseURL is required for registry type %d (ProGet)" , portainer . ProGetRegistry )
2018-12-09 16:49:27 +13:00
}
2021-07-01 14:57:15 +12:00
2018-06-11 15:13:19 +02:00
return nil
}
2021-02-23 05:21:39 +02:00
// @id RegistryCreate
// @summary Create a new registry
// @description Create a new registry.
2021-10-12 12:12:08 +13:00
// @description **Access policy**: restricted
2021-02-23 05:21:39 +02:00
// @tags registries
2021-11-30 15:31:16 +13:00
// @security ApiKeyAuth
2021-02-23 05:21:39 +02:00
// @security jwt
// @accept json
// @produce json
// @param body body registryCreatePayload true "Registry details"
// @success 200 {object} portainer.Registry "Success"
// @failure 400 "Invalid request"
2024-04-19 15:19:13 +12:00
// @failure 409 "Another registry with the same name or same URL & credentials already exists"
2021-02-23 05:21:39 +02:00
// @failure 500 "Server error"
// @router /registries [post]
2018-06-11 15:13:19 +02:00
func ( handler * Handler ) registryCreate ( w http . ResponseWriter , r * http . Request ) * httperror . HandlerError {
2021-07-14 11:15:21 +02:00
securityContext , err := security . RetrieveRestrictedRequestContext ( r )
if err != nil {
2022-09-14 20:42:39 -03:00
return httperror . InternalServerError ( "Unable to retrieve info from request context" , err )
2021-07-14 11:15:21 +02:00
}
if ! securityContext . IsAdmin {
2022-09-14 20:42:39 -03:00
return httperror . Forbidden ( "Permission denied to create registry" , httperrors . ErrResourceAccessDenied )
2021-07-14 11:15:21 +02:00
}
2018-06-11 15:13:19 +02:00
var payload registryCreatePayload
2021-07-14 11:15:21 +02:00
err = request . DecodeAndValidateJSONPayload ( r , & payload )
2018-06-11 15:13:19 +02:00
if err != nil {
2022-09-14 20:42:39 -03:00
return httperror . BadRequest ( "Invalid request payload" , err )
2018-06-11 15:13:19 +02:00
}
registry := & portainer . Registry {
2024-06-10 09:32:52 -03:00
Type : payload . Type ,
2021-07-14 11:15:21 +02:00
Name : payload . Name ,
URL : payload . URL ,
BaseURL : payload . BaseURL ,
Authentication : payload . Authentication ,
Username : payload . Username ,
Password : payload . Password ,
Gitlab : payload . Gitlab ,
Quay : payload . Quay ,
2021-12-01 13:18:57 +13:00
RegistryAccesses : portainer . RegistryAccesses { } ,
Ecr : payload . Ecr ,
2021-07-14 11:15:21 +02:00
}
2023-05-30 10:47:44 +07:00
registry . ManagementConfiguration = syncConfig ( registry )
2023-06-22 18:28:07 -03:00
registries , err := handler . DataStore . Registry ( ) . ReadAll ( )
2021-07-14 11:15:21 +02:00
if err != nil {
2022-09-14 20:42:39 -03:00
return httperror . InternalServerError ( "Unable to retrieve registries from the database" , err )
2021-07-14 11:15:21 +02:00
}
for _ , r := range registries {
2022-04-07 22:58:26 +02:00
if r . Name == registry . Name {
2023-10-05 11:26:24 +03:00
return httperror . Conflict ( "Another registry with the same name already exists" , errors . New ( "A registry is already defined with this name" ) )
2022-04-07 22:58:26 +02:00
}
2021-07-14 11:15:21 +02:00
if handler . registriesHaveSameURLAndCredentials ( & r , registry ) {
2023-10-05 11:26:24 +03:00
return httperror . Conflict ( "Another registry with the same URL and credentials already exists" , errors . New ( "A registry is already defined for this URL and credentials" ) )
2021-07-14 11:15:21 +02:00
}
2018-06-11 15:13:19 +02:00
}
2022-09-28 14:56:32 -03:00
err = handler . DataStore . Registry ( ) . Create ( registry )
2018-06-11 15:13:19 +02:00
if err != nil {
2022-09-14 20:42:39 -03:00
return httperror . InternalServerError ( "Unable to persist the registry inside the database" , err )
2018-06-11 15:13:19 +02:00
}
2021-07-14 11:15:21 +02:00
hideFields ( registry , true )
2018-06-11 15:13:19 +02:00
return response . JSON ( w , registry )
}