2018-06-11 13:13:19 +00:00
package registries
import (
2020-07-07 21:57:52 +00:00
"errors"
2021-07-01 02:57:15 +00:00
"fmt"
2018-06-11 13:13:19 +00:00
"net/http"
"github.com/asaskevich/govalidator"
2018-09-10 10:01:38 +00:00
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
2021-02-23 03:21:39 +00:00
portainer "github.com/portainer/portainer/api"
2021-07-14 09:15:21 +00:00
httperrors "github.com/portainer/portainer/api/http/errors"
"github.com/portainer/portainer/api/http/security"
2018-06-11 13:13:19 +00:00
)
type registryCreatePayload struct {
2021-02-23 03:21:39 +00:00
// Name that will be used to identify this registry
Name string ` example:"my-registry" validate:"required" `
2021-12-01 00:18:57 +00: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 03:21:39 +00:00
// URL or IP address of the Docker registry
2021-07-01 02:57:15 +00: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 03:21:39 +00: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-12 23:47:35 +00:00
// Quay specific details, required when type = 1
Quay portainer . QuayRegistryData
2021-12-01 00:18:57 +00:00
// ECR specific details, required when type = 7
Ecr portainer . EcrData
2018-06-11 13:13:19 +00:00
}
2021-07-01 02:57:15 +00:00
func ( payload * registryCreatePayload ) Validate ( _ * http . Request ) error {
2018-06-11 13:13:19 +00:00
if govalidator . IsNull ( payload . Name ) {
2020-07-07 21:57:52 +00:00
return errors . New ( "Invalid registry name" )
2018-06-11 13:13:19 +00:00
}
if govalidator . IsNull ( payload . URL ) {
2020-07-07 21:57:52 +00:00
return errors . New ( "Invalid registry URL" )
2018-06-11 13:13:19 +00:00
}
2021-12-01 00:18:57 +00:00
if payload . Authentication {
if 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 . EcrRegistry {
if govalidator . IsNull ( payload . Ecr . Region ) {
return errors . New ( "invalid credentials: access key ID, secret access key and region must be specified when authentication is enabled" )
}
}
2018-06-11 13:13:19 +00:00
}
2021-07-01 02:57:15 +00:00
switch payload . Type {
2021-12-01 00:18:57 +00:00
case portainer . QuayRegistry , portainer . AzureRegistry , portainer . CustomRegistry , portainer . GitlabRegistry , portainer . ProGetRegistry , portainer . DockerHubRegistry , portainer . EcrRegistry :
2021-07-01 02:57:15 +00:00
default :
2021-12-01 00:18:57 +00: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 02:57:15 +00:00
}
if payload . Type == portainer . ProGetRegistry && payload . BaseURL == "" {
return fmt . Errorf ( "BaseURL is required for registry type %d (ProGet)" , portainer . ProGetRegistry )
2018-12-09 03:49:27 +00:00
}
2021-07-01 02:57:15 +00:00
2018-06-11 13:13:19 +00:00
return nil
}
2021-02-23 03:21:39 +00:00
// @id RegistryCreate
// @summary Create a new registry
// @description Create a new registry.
2021-10-11 23:12:08 +00:00
// @description **Access policy**: restricted
2021-02-23 03:21:39 +00:00
// @tags registries
2021-11-30 02:31:16 +00:00
// @security ApiKeyAuth
2021-02-23 03:21:39 +00:00
// @security jwt
// @accept json
// @produce json
// @param body body registryCreatePayload true "Registry details"
// @success 200 {object} portainer.Registry "Success"
// @failure 400 "Invalid request"
// @failure 500 "Server error"
// @router /registries [post]
2018-06-11 13:13:19 +00:00
func ( handler * Handler ) registryCreate ( w http . ResponseWriter , r * http . Request ) * httperror . HandlerError {
2021-07-14 09:15:21 +00:00
securityContext , err := security . RetrieveRestrictedRequestContext ( r )
if err != nil {
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to retrieve info from request context" , err }
}
if ! securityContext . IsAdmin {
return & httperror . HandlerError { http . StatusForbidden , "Permission denied to create registry" , httperrors . ErrResourceAccessDenied }
}
2018-06-11 13:13:19 +00:00
var payload registryCreatePayload
2021-07-14 09:15:21 +00:00
err = request . DecodeAndValidateJSONPayload ( r , & payload )
2018-06-11 13:13:19 +00:00
if err != nil {
return & httperror . HandlerError { http . StatusBadRequest , "Invalid request payload" , err }
}
registry := & portainer . Registry {
2021-07-14 09:15:21 +00:00
Type : portainer . RegistryType ( payload . Type ) ,
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 00:18:57 +00:00
RegistryAccesses : portainer . RegistryAccesses { } ,
Ecr : payload . Ecr ,
2021-07-14 09:15:21 +00:00
}
registries , err := handler . DataStore . Registry ( ) . Registries ( )
if err != nil {
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to retrieve registries from the database" , err }
}
for _ , r := range registries {
if handler . registriesHaveSameURLAndCredentials ( & r , registry ) {
return & httperror . HandlerError { http . StatusConflict , "Another registry with the same URL and credentials already exists" , errors . New ( "A registry is already defined for this URL and credentials" ) }
}
2018-06-11 13:13:19 +00:00
}
2020-05-20 05:23:15 +00:00
err = handler . DataStore . Registry ( ) . CreateRegistry ( registry )
2018-06-11 13:13:19 +00:00
if err != nil {
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to persist the registry inside the database" , err }
}
2021-07-14 09:15:21 +00:00
hideFields ( registry , true )
2018-06-11 13:13:19 +00:00
return response . JSON ( w , registry )
}