Introduce deploymentkeys

pull/2972/head
ssbkang 2019-06-26 23:15:48 +12:00
parent 6591498ab9
commit ba9838296e
10 changed files with 314 additions and 3 deletions

View File

@ -7,6 +7,7 @@ import (
"github.com/boltdb/bolt"
"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/endpoint"
"github.com/portainer/portainer/api/bolt/endpointgroup"
@ -39,6 +40,7 @@ type Store struct {
checkForDataMigration bool
fileService portainer.FileService
RoleService *role.Service
DeploymentKeyService *deploymentkey.Service
DockerHubService *dockerhub.Service
EndpointGroupService *endpointgroup.Service
EndpointService *endpoint.Service
@ -148,6 +150,12 @@ func (store *Store) initServices() error {
}
store.RoleService = authorizationsetService
deploymentkeyService, err := deploymentkey.NewService(store.db)
if err != nil {
return err
}
store.DeploymentKeyService = deploymentkeyService
dockerhubService, err := dockerhub.NewService(store.db)
if err != nil {
return err

View File

@ -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)
})
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -9,6 +9,7 @@ import (
"github.com/portainer/portainer/api/http/handler/roles"
"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/endpointgroups"
"github.com/portainer/portainer/api/http/handler/endpointproxy"
@ -34,6 +35,7 @@ import (
// Handler is a collection of all the service handlers.
type Handler struct {
AuthHandler *auth.Handler
DeploymentKeyHandler *deploymentkeys.Handler
DockerHubHandler *dockerhub.Handler
EndpointGroupHandler *endpointgroups.Handler
EndpointHandler *endpoints.Handler
@ -63,6 +65,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch {
case strings.HasPrefix(r.URL.Path, "/api/auth"):
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"):
http.StripPrefix("/api", h.DockerHubHandler).ServeHTTP(w, r)
case strings.HasPrefix(r.URL.Path, "/api/endpoint_groups"):

View File

@ -3,10 +3,10 @@
package proxy
import (
"github.com/Microsoft/go-winio"
"net"
"net/http"
"github.com/Microsoft/go-winio"
portainer "github.com/portainer/portainer/api"
)

View File

@ -216,6 +216,18 @@ type (
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
// Docker Hub
DockerHub struct {
@ -674,6 +686,15 @@ type (
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 interface {
DockerHub() (*DockerHub, error)
@ -876,7 +897,7 @@ const (
PortainerAgentSignatureMessage = "Portainer-App"
// SupportedDockerAPIVersion is the minimum Docker API version supported by Portainer
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"
)