mirror of https://github.com/portainer/portainer
Introduce deploymentkeys
parent
6591498ab9
commit
ba9838296e
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/boltdb/bolt"
|
"github.com/boltdb/bolt"
|
||||||
"github.com/portainer/portainer/api"
|
"github.com/portainer/portainer/api"
|
||||||
|
"github.com/portainer/portainer/api/bolt/deploymentkey"
|
||||||
"github.com/portainer/portainer/api/bolt/dockerhub"
|
"github.com/portainer/portainer/api/bolt/dockerhub"
|
||||||
"github.com/portainer/portainer/api/bolt/endpoint"
|
"github.com/portainer/portainer/api/bolt/endpoint"
|
||||||
"github.com/portainer/portainer/api/bolt/endpointgroup"
|
"github.com/portainer/portainer/api/bolt/endpointgroup"
|
||||||
|
@ -39,6 +40,7 @@ type Store struct {
|
||||||
checkForDataMigration bool
|
checkForDataMigration bool
|
||||||
fileService portainer.FileService
|
fileService portainer.FileService
|
||||||
RoleService *role.Service
|
RoleService *role.Service
|
||||||
|
DeploymentKeyService *deploymentkey.Service
|
||||||
DockerHubService *dockerhub.Service
|
DockerHubService *dockerhub.Service
|
||||||
EndpointGroupService *endpointgroup.Service
|
EndpointGroupService *endpointgroup.Service
|
||||||
EndpointService *endpoint.Service
|
EndpointService *endpoint.Service
|
||||||
|
@ -148,6 +150,12 @@ func (store *Store) initServices() error {
|
||||||
}
|
}
|
||||||
store.RoleService = authorizationsetService
|
store.RoleService = authorizationsetService
|
||||||
|
|
||||||
|
deploymentkeyService, err := deploymentkey.NewService(store.db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
store.DeploymentKeyService = deploymentkeyService
|
||||||
|
|
||||||
dockerhubService, err := dockerhub.NewService(store.db)
|
dockerhubService, err := dockerhub.NewService(store.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
package deploymentkey
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/portainer/portainer/api"
|
||||||
|
"github.com/portainer/portainer/api/bolt/internal"
|
||||||
|
|
||||||
|
"github.com/boltdb/bolt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// BucketName represents the name of the bucket where this service stores data.
|
||||||
|
BucketName = "deploymentkey"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Service represents a service for managing deploymentkey data.
|
||||||
|
type Service struct {
|
||||||
|
db *bolt.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewService creates a new instance of a service.
|
||||||
|
func NewService(db *bolt.DB) (*Service, error) {
|
||||||
|
err := internal.CreateBucket(db, BucketName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Service{
|
||||||
|
db: db,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeploymentKeys return all the deployment keys that are created.
|
||||||
|
func (service *Service) DeploymentKeys() ([]portainer.DeploymentKey, error) {
|
||||||
|
var deploymentkeys = make([]portainer.DeploymentKey, 0)
|
||||||
|
|
||||||
|
err := service.db.View(func(tx *bolt.Tx) error {
|
||||||
|
bucket := tx.Bucket([]byte(BucketName))
|
||||||
|
|
||||||
|
cursor := bucket.Cursor()
|
||||||
|
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
||||||
|
var deploymentkey portainer.DeploymentKey
|
||||||
|
err := internal.UnmarshalObject(v, &deploymentkey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
deploymentkeys = append(deploymentkeys, deploymentkey)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return deploymentkeys, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeploymentKey returns the deployment key by deployment key ID.
|
||||||
|
func (service *Service) DeploymentKey(ID portainer.DeploymentKeyID) (*portainer.DeploymentKey, error) {
|
||||||
|
var deploymentkey portainer.DeploymentKey
|
||||||
|
identifier := internal.Itob(int(ID))
|
||||||
|
|
||||||
|
err := internal.GetObject(service.db, BucketName, identifier, &deploymentkey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deploymentkey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeploymentKeyByName returns a deploymentkey by name.
|
||||||
|
func (service *Service) DeploymentKeyByName(name string) (*portainer.DeploymentKey, error) {
|
||||||
|
var deploymentkey *portainer.DeploymentKey
|
||||||
|
|
||||||
|
err := service.db.View(func(tx *bolt.Tx) error {
|
||||||
|
bucket := tx.Bucket([]byte(BucketName))
|
||||||
|
|
||||||
|
cursor := bucket.Cursor()
|
||||||
|
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
||||||
|
var t portainer.DeploymentKey
|
||||||
|
err := internal.UnmarshalObject(v, &t)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Name == name {
|
||||||
|
deploymentkey = &t
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if deploymentkey == nil {
|
||||||
|
return portainer.ErrObjectNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return deploymentkey, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteDeploymentKey deletes a deployment key.
|
||||||
|
func (service *Service) DeleteDeploymentKey(ID portainer.DeploymentKeyID) error {
|
||||||
|
identifier := internal.Itob(int(ID))
|
||||||
|
return internal.DeleteObject(service.db, BucketName, identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDeploymentKey creates a deployment key.
|
||||||
|
func (service *Service) CreateDeploymentKey(deploymentkey *portainer.DeploymentKey) error {
|
||||||
|
return service.db.Update(func(tx *bolt.Tx) error {
|
||||||
|
bucket := tx.Bucket([]byte(BucketName))
|
||||||
|
|
||||||
|
id, _ := bucket.NextSequence()
|
||||||
|
deploymentkey.ID = portainer.DeploymentKeyID(id)
|
||||||
|
|
||||||
|
data, err := internal.MarshalObject(deploymentkey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bucket.Put(internal.Itob(int(deploymentkey.ID)), data)
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package deploymentkeys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/asaskevich/govalidator"
|
||||||
|
httperror "github.com/portainer/libhttp/error"
|
||||||
|
"github.com/portainer/libhttp/request"
|
||||||
|
"github.com/portainer/libhttp/response"
|
||||||
|
"github.com/portainer/portainer/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
type deploymentKeyCreatePayload struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (payload *deploymentKeyCreatePayload) Validate(r *http.Request) error {
|
||||||
|
if govalidator.IsNull(payload.Name) {
|
||||||
|
return portainer.Error("Invalid deploymentkey name")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *Handler) deploymentkeyCreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||||
|
var payload deploymentKeyCreatePayload
|
||||||
|
err := request.DecodeAndValidateJSONPayload(r, &payload)
|
||||||
|
if err != nil {
|
||||||
|
return &httperror.HandlerError{http.StatusBadRequest, "Invalid request payload", err}
|
||||||
|
}
|
||||||
|
|
||||||
|
deploymentkey, err := handler.DeploymentKeyService.DeploymentKeyByName(payload.Name)
|
||||||
|
if err == portainer.ErrObjectNotFound {
|
||||||
|
return &httperror.HandlerError{http.StatusNotFound, "Unable to find a deployment key with the specified identifier inside the database", err}
|
||||||
|
} else if err != nil {
|
||||||
|
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a deployment key with the specified identifier inside the database", err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a function to call and create public key and private key
|
||||||
|
|
||||||
|
deploymentkey = &portainer.DeploymentKey{
|
||||||
|
Name: payload.Name,
|
||||||
|
PublicKey: "SHA256:hellotherepublic",
|
||||||
|
PrivateKey: "SHA256:hellothereprivate",
|
||||||
|
}
|
||||||
|
|
||||||
|
err = handler.DeploymentKeyService.CreateDeploymentKey(deploymentkey)
|
||||||
|
if err != nil {
|
||||||
|
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist the deployment key inside the database", err}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.JSON(w, deploymentkey)
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package deploymentkeys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
httperror "github.com/portainer/libhttp/error"
|
||||||
|
"github.com/portainer/libhttp/request"
|
||||||
|
"github.com/portainer/libhttp/response"
|
||||||
|
"github.com/portainer/portainer/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DELETE request on /api/webhook/:serviceID
|
||||||
|
func (handler *Handler) deploymentkeyDelete(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||||
|
id, err := request.RetrieveNumericRouteVariableValue(r, "id")
|
||||||
|
if err != nil {
|
||||||
|
return &httperror.HandlerError{http.StatusBadRequest, "Invalid deployment key id", err}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = handler.DeploymentKeyService.DeleteDeploymentKey(portainer.DeploymentKeyID(id))
|
||||||
|
if err != nil {
|
||||||
|
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to remove the deployment key from the database", err}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Empty(w)
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package deploymentkeys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
httperror "github.com/portainer/libhttp/error"
|
||||||
|
"github.com/portainer/libhttp/request"
|
||||||
|
"github.com/portainer/libhttp/response"
|
||||||
|
"github.com/portainer/portainer/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GET request on /api/deployment_keys/:id
|
||||||
|
func (handler *Handler) deploymentkeyInspect(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||||
|
deploymentkeyID, err := request.RetrieveNumericRouteVariableValue(r, "id")
|
||||||
|
if err != nil {
|
||||||
|
return &httperror.HandlerError{http.StatusBadRequest, "Invalid deploymentkey identifier route variable", err}
|
||||||
|
}
|
||||||
|
|
||||||
|
deploymentkey, err := handler.DeploymentKeyService.DeploymentKey(portainer.DeploymentKeyID(deploymentkeyID))
|
||||||
|
if err == portainer.ErrObjectNotFound {
|
||||||
|
return &httperror.HandlerError{http.StatusNotFound, "Unable to find a deployment key with the specified identifier inside the database", err}
|
||||||
|
} else if err != nil {
|
||||||
|
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a deployment key with the specified identifier inside the database", err}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.JSON(w, deploymentkey)
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package deploymentkeys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
httperror "github.com/portainer/libhttp/error"
|
||||||
|
"github.com/portainer/libhttp/response"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GET request on /api/deployment_keys
|
||||||
|
func (handler *Handler) deploymentkeyList(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||||
|
deploymentkeys, err := handler.DeploymentKeyService.DeploymentKeys()
|
||||||
|
if err != nil {
|
||||||
|
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve deploymentkeys from the database", err}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.JSON(w, deploymentkeys)
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package deploymentkeys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
httperror "github.com/portainer/libhttp/error"
|
||||||
|
portainer "github.com/portainer/portainer/api"
|
||||||
|
"github.com/portainer/portainer/api/http/security"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handler is the HTTP handler used to handle webhook operations.
|
||||||
|
type Handler struct {
|
||||||
|
*mux.Router
|
||||||
|
DeploymentKeyService portainer.DeploymentKeyService
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHandler creates a handler to manage settings operations.
|
||||||
|
func NewHandler(bouncer *security.RequestBouncer) *Handler {
|
||||||
|
h := &Handler{
|
||||||
|
Router: mux.NewRouter(),
|
||||||
|
}
|
||||||
|
h.Handle("/deployment_keys",
|
||||||
|
// bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.deploymentkeyCreate))).Methods(http.MethodPost)
|
||||||
|
bouncer.PublicAccess(httperror.LoggerHandler(h.deploymentkeyCreate))).Methods(http.MethodPost)
|
||||||
|
h.Handle("/deployment_keys",
|
||||||
|
// bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.deploymentkeyList))).Methods(http.MethodGet)
|
||||||
|
bouncer.PublicAccess(httperror.LoggerHandler(h.deploymentkeyList))).Methods(http.MethodGet)
|
||||||
|
h.Handle("/deployment_keys/{id}",
|
||||||
|
// bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.deploymentkeyList))).Methods(http.MethodGet)
|
||||||
|
bouncer.PublicAccess(httperror.LoggerHandler(h.deploymentkeyInspect))).Methods(http.MethodGet)
|
||||||
|
h.Handle("/deployment_keys/{id}",
|
||||||
|
// bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.deploymentkeyDelete))).Methods(http.MethodDelete)
|
||||||
|
bouncer.PublicAccess(httperror.LoggerHandler(h.deploymentkeyDelete))).Methods(http.MethodDelete)
|
||||||
|
return h
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/portainer/portainer/api/http/handler/roles"
|
"github.com/portainer/portainer/api/http/handler/roles"
|
||||||
|
|
||||||
"github.com/portainer/portainer/api/http/handler/auth"
|
"github.com/portainer/portainer/api/http/handler/auth"
|
||||||
|
"github.com/portainer/portainer/api/http/handler/deploymentkeys"
|
||||||
"github.com/portainer/portainer/api/http/handler/dockerhub"
|
"github.com/portainer/portainer/api/http/handler/dockerhub"
|
||||||
"github.com/portainer/portainer/api/http/handler/endpointgroups"
|
"github.com/portainer/portainer/api/http/handler/endpointgroups"
|
||||||
"github.com/portainer/portainer/api/http/handler/endpointproxy"
|
"github.com/portainer/portainer/api/http/handler/endpointproxy"
|
||||||
|
@ -34,6 +35,7 @@ import (
|
||||||
// Handler is a collection of all the service handlers.
|
// Handler is a collection of all the service handlers.
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
AuthHandler *auth.Handler
|
AuthHandler *auth.Handler
|
||||||
|
DeploymentKeyHandler *deploymentkeys.Handler
|
||||||
DockerHubHandler *dockerhub.Handler
|
DockerHubHandler *dockerhub.Handler
|
||||||
EndpointGroupHandler *endpointgroups.Handler
|
EndpointGroupHandler *endpointgroups.Handler
|
||||||
EndpointHandler *endpoints.Handler
|
EndpointHandler *endpoints.Handler
|
||||||
|
@ -63,6 +65,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(r.URL.Path, "/api/auth"):
|
case strings.HasPrefix(r.URL.Path, "/api/auth"):
|
||||||
http.StripPrefix("/api", h.AuthHandler).ServeHTTP(w, r)
|
http.StripPrefix("/api", h.AuthHandler).ServeHTTP(w, r)
|
||||||
|
case strings.HasPrefix(r.URL.Path, "/api/deployment_keys"):
|
||||||
|
http.StripPrefix("/api", h.DeploymentKeyHandler).ServeHTTP(w, r)
|
||||||
case strings.HasPrefix(r.URL.Path, "/api/dockerhub"):
|
case strings.HasPrefix(r.URL.Path, "/api/dockerhub"):
|
||||||
http.StripPrefix("/api", h.DockerHubHandler).ServeHTTP(w, r)
|
http.StripPrefix("/api", h.DockerHubHandler).ServeHTTP(w, r)
|
||||||
case strings.HasPrefix(r.URL.Path, "/api/endpoint_groups"):
|
case strings.HasPrefix(r.URL.Path, "/api/endpoint_groups"):
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
package proxy
|
package proxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/Microsoft/go-winio"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"github.com/Microsoft/go-winio"
|
|
||||||
|
|
||||||
portainer "github.com/portainer/portainer/api"
|
portainer "github.com/portainer/portainer/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -216,6 +216,18 @@ type (
|
||||||
TLSConfig TLSConfiguration `json:"TLSConfig"`
|
TLSConfig TLSConfiguration `json:"TLSConfig"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeploymentKeyID represents
|
||||||
|
DeploymentKeyID int
|
||||||
|
|
||||||
|
// DeploymentKey represents the SSH key details that will be used to
|
||||||
|
// connect to GitHub for deployments based on private key clone
|
||||||
|
DeploymentKey struct {
|
||||||
|
ID DeploymentKeyID `json:"Id`
|
||||||
|
Name string `json:"Name"`
|
||||||
|
PublicKey string `json:"PublicKey"`
|
||||||
|
PrivateKey string `json:"PrivateKey"`
|
||||||
|
}
|
||||||
|
|
||||||
// DockerHub represents all the required information to connect and use the
|
// DockerHub represents all the required information to connect and use the
|
||||||
// Docker Hub
|
// Docker Hub
|
||||||
DockerHub struct {
|
DockerHub struct {
|
||||||
|
@ -674,6 +686,15 @@ type (
|
||||||
GetNextIdentifier() int
|
GetNextIdentifier() int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeploymentKeyService represents a service for managing the DeploymentKey object
|
||||||
|
DeploymentKeyService interface {
|
||||||
|
DeploymentKey(ID DeploymentKeyID) (*DeploymentKey, error)
|
||||||
|
DeploymentKeyByName(name string) (*DeploymentKey, error)
|
||||||
|
DeploymentKeys() ([]DeploymentKey, error)
|
||||||
|
DeleteDeploymentKey(ID DeploymentKeyID) error
|
||||||
|
CreateDeploymentKey(deploymentkey *DeploymentKey) error
|
||||||
|
}
|
||||||
|
|
||||||
// DockerHubService represents a service for managing the DockerHub object
|
// DockerHubService represents a service for managing the DockerHub object
|
||||||
DockerHubService interface {
|
DockerHubService interface {
|
||||||
DockerHub() (*DockerHub, error)
|
DockerHub() (*DockerHub, error)
|
||||||
|
@ -876,7 +897,7 @@ const (
|
||||||
PortainerAgentSignatureMessage = "Portainer-App"
|
PortainerAgentSignatureMessage = "Portainer-App"
|
||||||
// SupportedDockerAPIVersion is the minimum Docker API version supported by Portainer
|
// SupportedDockerAPIVersion is the minimum Docker API version supported by Portainer
|
||||||
SupportedDockerAPIVersion = "1.24"
|
SupportedDockerAPIVersion = "1.24"
|
||||||
// ExtensionServer represents the server used by Portainer to communicate with extensions
|
// ExtensionServer represents the server used by Portainer to communicate with extensions
|
||||||
ExtensionServer = "localhost"
|
ExtensionServer = "localhost"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue