diff --git a/cmd/mdmctl/apply.go b/cmd/mdmctl/apply.go index d260af21..02cbae22 100644 --- a/cmd/mdmctl/apply.go +++ b/cmd/mdmctl/apply.go @@ -148,7 +148,7 @@ func (cmd *applyCommand) applyBlueprint(args []string) error { } ctx := context.Background() - err = cmd.applysvc.ApplyBlueprint(ctx, &blpt) + err = cmd.blueprintsvc.ApplyBlueprint(ctx, &blpt) if err != nil { return err } diff --git a/cmd/mdmctl/get.go b/cmd/mdmctl/get.go index 1ab0f036..9e2eae0c 100644 --- a/cmd/mdmctl/get.go +++ b/cmd/mdmctl/get.go @@ -17,6 +17,7 @@ import ( "github.com/micromdm/micromdm/pkg/crypto" "github.com/micromdm/micromdm/platform/api/server/list" + "github.com/micromdm/micromdm/platform/blueprint" "github.com/micromdm/micromdm/platform/profile" ) @@ -232,7 +233,7 @@ func (cmd *getCommand) getBlueprints(args []string) error { } ctx := context.Background() - blueprints, err := cmd.list.GetBlueprints(ctx, list.GetBlueprintsOption{FilterName: *flBlueprintName}) + blueprints, err := cmd.blueprintsvc.GetBlueprints(ctx, blueprint.GetBlueprintsOption{FilterName: *flBlueprintName}) if err != nil { return err } diff --git a/cmd/mdmctl/mdmctl b/cmd/mdmctl/mdmctl new file mode 100755 index 00000000..8b02bfc4 Binary files /dev/null and b/cmd/mdmctl/mdmctl differ diff --git a/cmd/mdmctl/remove_blueprints.go b/cmd/mdmctl/remove_blueprints.go index e70d7a5a..9c3d4259 100644 --- a/cmd/mdmctl/remove_blueprints.go +++ b/cmd/mdmctl/remove_blueprints.go @@ -18,7 +18,7 @@ func (cmd *removeCommand) removeBlueprints(args []string) error { } ctx := context.Background() - err := cmd.remove.RemoveBlueprints(ctx, strings.Split(*flBlueprintName, ",")) + err := cmd.blueprintsvc.RemoveBlueprints(ctx, strings.Split(*flBlueprintName, ",")) if err != nil { return err } diff --git a/cmd/mdmctl/setup.go b/cmd/mdmctl/setup.go index 821f6d9c..e683882a 100644 --- a/cmd/mdmctl/setup.go +++ b/cmd/mdmctl/setup.go @@ -7,14 +7,16 @@ import ( "github.com/micromdm/micromdm/platform/api/server/apply" "github.com/micromdm/micromdm/platform/api/server/list" "github.com/micromdm/micromdm/platform/api/server/remove" + "github.com/micromdm/micromdm/platform/blueprint" "github.com/micromdm/micromdm/platform/profile" ) type remoteServices struct { - profilesvc profile.Service - applysvc apply.Service - list list.Service - remove remove.Service + profilesvc profile.Service + blueprintsvc blueprint.Service + applysvc apply.Service + list list.Service + remove remove.Service } func setupClient(logger log.Logger) (*remoteServices, error) { @@ -22,20 +24,28 @@ func setupClient(logger log.Logger) (*remoteServices, error) { if err != nil { return nil, err } - applysvc, err := apply.NewClient( - cfg.ServerURL, logger, cfg.APIToken, + + profilesvc, err := profile.NewHTTPClient( + cfg.ServerURL, cfg.APIToken, logger, httptransport.SetClient(skipVerifyHTTPClient(cfg.SkipVerify))) if err != nil { return nil, err } - profilesvc, err := profile.NewHTTPClient( + blueprintsvc, err := blueprint.NewHTTPClient( cfg.ServerURL, cfg.APIToken, logger, httptransport.SetClient(skipVerifyHTTPClient(cfg.SkipVerify))) if err != nil { return nil, err } + applysvc, err := apply.NewClient( + cfg.ServerURL, logger, cfg.APIToken, + httptransport.SetClient(skipVerifyHTTPClient(cfg.SkipVerify))) + if err != nil { + return nil, err + } + listsvc, err := list.NewClient( cfg.ServerURL, logger, cfg.APIToken, httptransport.SetClient(skipVerifyHTTPClient(cfg.SkipVerify))) @@ -51,9 +61,10 @@ func setupClient(logger log.Logger) (*remoteServices, error) { } return &remoteServices{ - profilesvc: profilesvc, - applysvc: applysvc, - list: listsvc, - remove: rmsvc, + profilesvc: profilesvc, + blueprintsvc: blueprintsvc, + applysvc: applysvc, + list: listsvc, + remove: rmsvc, }, nil } diff --git a/cmd/micromdm/serve.go b/cmd/micromdm/serve.go index eb50b011..150aca4a 100644 --- a/cmd/micromdm/serve.go +++ b/cmd/micromdm/serve.go @@ -49,6 +49,7 @@ import ( "github.com/micromdm/micromdm/platform/apns" "github.com/micromdm/micromdm/platform/appstore" "github.com/micromdm/micromdm/platform/blueprint" + blueprintbuiltin "github.com/micromdm/micromdm/platform/blueprint/builtin" "github.com/micromdm/micromdm/platform/command" "github.com/micromdm/micromdm/platform/config" "github.com/micromdm/micromdm/platform/deptoken" @@ -186,7 +187,7 @@ func serve(args []string) error { stdlog.Fatalf("enrollment service: %s", sm.err) } - bpDB, err := blueprint.NewDB(sm.db, sm.profileDB, userDB) + bpDB, err := blueprintbuiltin.NewDB(sm.db, sm.profileDB, userDB) if err != nil { stdlog.Fatal(err) } @@ -277,15 +278,21 @@ func serve(args []string) error { profileEndpoints := profile.MakeServerEndpoints(profilesvc) + var blueprintsvc blueprint.Service + { + blueprintsvc = blueprint.New(bpDB) + } + + blueprintEndpoints := blueprint.MakeServerEndpoints(blueprintsvc) + var listsvc list.Service { l := &list.ListService{ - DEPClient: dc, - Devices: devDB, - Tokens: tokenDB, - Blueprints: bpDB, - Apps: appDB, - Users: userDB, + DEPClient: dc, + Devices: devDB, + Tokens: tokenDB, + Apps: appDB, + Users: userDB, } listsvc = l @@ -301,7 +308,6 @@ func serve(args []string) error { listEndpoints := list.Endpoints{ ListDevicesEndpoint: listDevicesEndpoint, GetDEPTokensEndpoint: list.MakeGetDEPTokensEndpoint(listsvc), - GetBlueprintsEndpoint: list.MakeGetBlueprintsEndpoint(listsvc), GetDEPAccountInfoEndpoint: list.MakeGetDEPAccountInfoEndpoint(listsvc), GetDEPProfileEndpoint: list.MakeGetDEPProfileEndpoint(listsvc), GetDEPDeviceEndpoint: list.MakeGetDEPDeviceDetailsEndpoint(listsvc), @@ -313,7 +319,6 @@ func serve(args []string) error { { l := &apply.ApplyService{ DEPClient: dc, - Blueprints: bpDB, Tokens: tokenDB, Apps: appDB, Users: userDB, @@ -325,11 +330,6 @@ func serve(args []string) error { } } - var applyBlueprintEndpoint endpoint.Endpoint - { - applyBlueprintEndpoint = apply.MakeApplyBlueprintEndpoint(applysvc) - } - var defineDEPProfileEndpoint endpoint.Endpoint { defineDEPProfileEndpoint = apply.MakeDefineDEPProfile(applysvc) @@ -346,7 +346,6 @@ func serve(args []string) error { } applyEndpoints := apply.Endpoints{ - ApplyBlueprintEndpoint: applyBlueprintEndpoint, ApplyDEPTokensEndpoint: apply.MakeApplyDEPTokensEndpoint(applysvc), DefineDEPProfileEndpoint: defineDEPProfileEndpoint, AppUploadEndpoint: appUploadEndpoint, @@ -358,7 +357,7 @@ func serve(args []string) error { listAPIHandlers := list.MakeHTTPHandlers(ctx, listEndpoints, connectOpts...) - rmsvc := &remove.RemoveService{Blueprints: bpDB, RemoveService: removeService} + rmsvc := &remove.RemoveService{RemoveService: removeService} removeAPIHandlers := remove.MakeHTTPHandlers(ctx, remove.MakeEndpoints(rmsvc), connectOpts...) connectHandlers := connect.MakeHTTPHandlers(ctx, connectEndpoints, connectOpts...) @@ -378,10 +377,12 @@ func serve(args []string) error { }) profilesHandler := profile.MakeHTTPHandler(profileEndpoints, logger) + blueprintsHandler := blueprint.MakeHTTPHandler(blueprintEndpoints, logger) // API commands. Only handled if the user provides an api key. if *flAPIKey != "" { r.Handle("/v1/profiles", apiAuthMiddleware(*flAPIKey, profilesHandler)) + r.Handle("/v1/blueprints", apiAuthMiddleware(*flAPIKey, blueprintsHandler)) r.Handle("/push/{udid}", apiAuthMiddleware(*flAPIKey, pushHandlers.PushHandler)) r.Handle("/v1/commands", apiAuthMiddleware(*flAPIKey, commandHandlers.NewCommandHandler)).Methods("POST") r.Handle("/v1/devices", apiAuthMiddleware(*flAPIKey, listAPIHandlers.ListDevicesHandler)).Methods("GET") @@ -389,9 +390,6 @@ func serve(args []string) error { r.Handle("/v1/devices/{udid}/unblock", apiAuthMiddleware(*flAPIKey, removeAPIHandlers.UnblockDeviceHandler)).Methods("POST") r.Handle("/v1/dep-tokens", apiAuthMiddleware(*flAPIKey, listAPIHandlers.GetDEPTokensHandler)).Methods("GET") r.Handle("/v1/dep-tokens", apiAuthMiddleware(*flAPIKey, applyAPIHandlers.DEPTokensHandler)).Methods("PUT") - r.Handle("/v1/blueprints", apiAuthMiddleware(*flAPIKey, listAPIHandlers.GetBlueprintsHandler)).Methods("GET") - r.Handle("/v1/blueprints", apiAuthMiddleware(*flAPIKey, applyAPIHandlers.BlueprintHandler)).Methods("PUT") - r.Handle("/v1/blueprints", apiAuthMiddleware(*flAPIKey, removeAPIHandlers.BlueprintHandler)).Methods("DELETE") r.Handle("/v1/dep/devices", apiAuthMiddleware(*flAPIKey, listAPIHandlers.GetDEPDeviceDetailsHandler)).Methods("GET") r.Handle("/v1/dep/account", apiAuthMiddleware(*flAPIKey, listAPIHandlers.GetDEPAccountInfoHandler)).Methods("GET") r.Handle("/v1/dep/profiles", apiAuthMiddleware(*flAPIKey, listAPIHandlers.GetDEPProfileHandler)).Methods("GET") diff --git a/platform/api/server/apply/client.go b/platform/api/server/apply/client.go index 9dc3dcdb..9e3657aa 100644 --- a/platform/api/server/apply/client.go +++ b/platform/api/server/apply/client.go @@ -16,16 +16,6 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra return nil, err } - var applyBlueprintEndpoint endpoint.Endpoint - { - applyBlueprintEndpoint = httptransport.NewClient( - "PUT", - copyURL(u, "/v1/blueprints"), - encodeRequestWithToken(token, EncodeHTTPGenericRequest), - DecodeBlueprintResponse, - opts..., - ).Endpoint() - } var applyDEPTokensEndpoint endpoint.Endpoint { applyDEPTokensEndpoint = httptransport.NewClient( @@ -82,7 +72,6 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra } return Endpoints{ - ApplyBlueprintEndpoint: applyBlueprintEndpoint, ApplyDEPTokensEndpoint: applyDEPTokensEndpoint, DefineDEPProfileEndpoint: defineDEPProfileEndpoint, AppUploadEndpoint: uploadAppEndpoint, diff --git a/platform/api/server/apply/endpoint.go b/platform/api/server/apply/endpoint.go index 0d2eeb11..69c92cd2 100644 --- a/platform/api/server/apply/endpoint.go +++ b/platform/api/server/apply/endpoint.go @@ -7,12 +7,10 @@ import ( "github.com/go-kit/kit/endpoint" "github.com/micromdm/dep" - "github.com/micromdm/micromdm/platform/blueprint" "github.com/micromdm/micromdm/platform/user" ) type Endpoints struct { - ApplyBlueprintEndpoint endpoint.Endpoint ApplyDEPTokensEndpoint endpoint.Endpoint DefineDEPProfileEndpoint endpoint.Endpoint AppUploadEndpoint endpoint.Endpoint @@ -67,15 +65,6 @@ func (e Endpoints) DefineDEPProfile(ctx context.Context, p *dep.Profile) (*dep.P return response.ProfileResponse, response.Err } -func (e Endpoints) ApplyBlueprint(ctx context.Context, bp *blueprint.Blueprint) error { - request := blueprintRequest{Blueprint: bp} - resp, err := e.ApplyBlueprintEndpoint(ctx, request) - if err != nil { - return err - } - return resp.(blueprintResponse).Err -} - func (e Endpoints) ApplyDEPToken(ctx context.Context, P7MContent []byte) error { req := depTokensRequest{P7MContent: P7MContent} resp, err := e.ApplyDEPTokensEndpoint(ctx, req) @@ -85,16 +74,6 @@ func (e Endpoints) ApplyDEPToken(ctx context.Context, P7MContent []byte) error { return resp.(depTokensResponse).Err } -func MakeApplyBlueprintEndpoint(svc Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(blueprintRequest) - err = svc.ApplyBlueprint(ctx, req.Blueprint) - return blueprintResponse{ - Err: err, - }, nil - } -} - func MakeApplyDEPTokensEndpoint(svc Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(depTokensRequest) @@ -161,16 +140,6 @@ type appUploadResponse struct { func (r appUploadResponse) error() error { return r.Err } -type blueprintRequest struct { - Blueprint *blueprint.Blueprint `json:"blueprint"` -} - -type blueprintResponse struct { - Err error `json:"err,omitempty"` -} - -func (r blueprintResponse) error() error { return r.Err } - type depTokensRequest struct { P7MContent []byte `json:"p7m_content"` } diff --git a/platform/api/server/apply/service.go b/platform/api/server/apply/service.go index 56574d6e..494b5cc3 100644 --- a/platform/api/server/apply/service.go +++ b/platform/api/server/apply/service.go @@ -17,7 +17,6 @@ import ( "github.com/pkg/errors" "github.com/micromdm/micromdm/platform/appstore" - "github.com/micromdm/micromdm/platform/blueprint" "github.com/micromdm/micromdm/platform/deptoken" "github.com/micromdm/micromdm/platform/pubsub" "github.com/micromdm/micromdm/platform/remove" @@ -25,7 +24,6 @@ import ( ) type Service interface { - ApplyBlueprint(ctx context.Context, bp *blueprint.Blueprint) error ApplyDEPToken(ctx context.Context, P7MContent []byte) error UploadApp(ctx context.Context, manifestName string, manifest io.Reader, pkgName string, pkg io.Reader) error ApplyUser(ctx context.Context, u user.User) (*user.User, error) @@ -37,10 +35,9 @@ type ApplyService struct { mtx sync.RWMutex DEPClient dep.Client - Blueprints *blueprint.DB - Tokens *deptoken.DB - Apps appstore.AppStore - Users *user.DB + Tokens *deptoken.DB + Apps appstore.AppStore + Users *user.DB *remove.RemoveService } @@ -105,10 +102,6 @@ func (svc *ApplyService) WatchTokenUpdates(pubsub pubsub.Subscriber) error { return nil } -func (svc *ApplyService) ApplyBlueprint(ctx context.Context, bp *blueprint.Blueprint) error { - return svc.Blueprints.Save(bp) -} - // unwrapSMIME removes the S/MIME-like wrapper around raw CMS/PKCS7 data func unwrapSMIME(smime []byte) ([]byte, error) { tr := textproto.NewReader(bufio.NewReader(bytes.NewReader(smime))) diff --git a/platform/api/server/apply/transport_http.go b/platform/api/server/apply/transport_http.go index 3cac1657..764f9ef0 100644 --- a/platform/api/server/apply/transport_http.go +++ b/platform/api/server/apply/transport_http.go @@ -16,7 +16,6 @@ import ( ) type HTTPHandlers struct { - BlueprintHandler http.Handler DEPTokensHandler http.Handler DefineDEPProfileHandler http.Handler AppUploadHandler http.Handler @@ -26,12 +25,6 @@ type HTTPHandlers struct { func MakeHTTPHandlers(ctx context.Context, endpoints Endpoints, opts ...httptransport.ServerOption) HTTPHandlers { h := HTTPHandlers{ - BlueprintHandler: httptransport.NewServer( - endpoints.ApplyBlueprintEndpoint, - decodeBlueprintRequest, - encodeResponse, - opts..., - ), DEPTokensHandler: httptransport.NewServer( endpoints.ApplyDEPTokensEndpoint, decodeDEPTokensRequest, @@ -86,14 +79,6 @@ func decodeDEPTokensRequest(ctx context.Context, r *http.Request) (interface{}, return req, nil } -func decodeBlueprintRequest(ctx context.Context, r *http.Request) (interface{}, error) { - var bpReq blueprintRequest - if err := json.NewDecoder(r.Body).Decode(&bpReq); err != nil { - return nil, err - } - return bpReq, nil -} - func decodeDEPProfileRequest(ctx context.Context, r *http.Request) (interface{}, error) { var req depProfileRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { @@ -221,15 +206,6 @@ func encodeBlockDeviceRequest(_ context.Context, r *http.Request, request interf return nil } -func DecodeBlueprintResponse(_ context.Context, r *http.Response) (interface{}, error) { - if r.StatusCode != http.StatusOK { - return nil, errorDecoder(r) - } - var resp blueprintResponse - err := json.NewDecoder(r.Body).Decode(&resp) - return resp, err -} - func DecodeDEPTokensResponse(_ context.Context, r *http.Response) (interface{}, error) { if r.StatusCode != http.StatusOK { return nil, errorDecoder(r) diff --git a/platform/api/server/list/client.go b/platform/api/server/list/client.go index a3f8d070..7d8b1d1c 100644 --- a/platform/api/server/list/client.go +++ b/platform/api/server/list/client.go @@ -36,16 +36,6 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra opts..., ).Endpoint() } - var getBlueprintsEndpoint endpoint.Endpoint - { - getBlueprintsEndpoint = httptransport.NewClient( - "GET", - copyURL(u, "/v1/blueprints"), - encodeRequestWithToken(token, EncodeHTTPGenericRequest), - DecodeGetBlueprintsResponse, - opts..., - ).Endpoint() - } var getDEPAccountInfoEndpoint endpoint.Endpoint { @@ -105,7 +95,6 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra return Endpoints{ ListDevicesEndpoint: listDevicesEndpoint, GetDEPTokensEndpoint: getDEPTokensEndpoint, - GetBlueprintsEndpoint: getBlueprintsEndpoint, GetDEPAccountInfoEndpoint: getDEPAccountInfoEndpoint, GetDEPDeviceEndpoint: getDEPDeviceDetailsEndpoint, GetDEPProfileEndpoint: getDEPProfilesEndpoint, diff --git a/platform/api/server/list/endpoint.go b/platform/api/server/list/endpoint.go index d7b9c35d..3b496e18 100644 --- a/platform/api/server/list/endpoint.go +++ b/platform/api/server/list/endpoint.go @@ -7,7 +7,6 @@ import ( "github.com/go-kit/kit/endpoint" "github.com/micromdm/dep" - "github.com/micromdm/micromdm/platform/blueprint" "github.com/micromdm/micromdm/platform/deptoken" "github.com/micromdm/micromdm/platform/user" ) @@ -15,7 +14,6 @@ import ( type Endpoints struct { ListDevicesEndpoint endpoint.Endpoint GetDEPTokensEndpoint endpoint.Endpoint - GetBlueprintsEndpoint endpoint.Endpoint GetDEPAccountInfoEndpoint endpoint.Endpoint GetDEPDeviceEndpoint endpoint.Endpoint GetDEPProfileEndpoint endpoint.Endpoint @@ -58,15 +56,6 @@ func (e Endpoints) GetDEPTokens(ctx context.Context) ([]deptoken.DEPToken, []byt return resp.(depTokenResponse).DEPTokens, resp.(depTokenResponse).DEPPubKey, nil } -func (e Endpoints) GetBlueprints(ctx context.Context, opt GetBlueprintsOption) ([]blueprint.Blueprint, error) { - request := blueprintsRequest{opt} - response, err := e.GetBlueprintsEndpoint(ctx, request.Opts) - if err != nil { - return nil, err - } - return response.(blueprintsResponse).Blueprints, response.(blueprintsResponse).Err -} - func (e Endpoints) GetDEPAccountInfo(ctx context.Context) (*dep.Account, error) { request := depAccountInforequest{} response, err := e.GetDEPAccountInfoEndpoint(ctx, request) @@ -138,17 +127,6 @@ func MakeGetDEPTokensEndpoint(svc Service) endpoint.Endpoint { } } -func MakeGetBlueprintsEndpoint(svc Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(blueprintsRequest) - blueprints, err := svc.GetBlueprints(ctx, req.Opts) - return blueprintsResponse{ - Blueprints: blueprints, - Err: err, - }, nil - } -} - func MakeGetDEPAccountInfoEndpoint(svc Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { account, err := svc.GetDEPAccountInfo(ctx) @@ -199,14 +177,6 @@ type depTokenResponse struct { Err error `json:"err,omitempty"` } -type blueprintsRequest struct{ Opts GetBlueprintsOption } -type blueprintsResponse struct { - Blueprints []blueprint.Blueprint `json:"blueprints"` - Err error `json:"err,omitempty"` -} - -func (r blueprintsResponse) error() error { return r.Err } - type depAccountInforequest struct{} type depAccountInfoResponse struct { *dep.Account diff --git a/platform/api/server/list/service.go b/platform/api/server/list/service.go index e7949ec0..c42e9851 100644 --- a/platform/api/server/list/service.go +++ b/platform/api/server/list/service.go @@ -11,10 +11,8 @@ import ( "github.com/pkg/errors" "github.com/micromdm/micromdm/platform/appstore" - "github.com/micromdm/micromdm/platform/blueprint" "github.com/micromdm/micromdm/platform/deptoken" "github.com/micromdm/micromdm/platform/device" - "github.com/micromdm/micromdm/platform/profile" "github.com/micromdm/micromdm/platform/pubsub" "github.com/micromdm/micromdm/platform/user" ) @@ -35,10 +33,6 @@ type ListUsersOption struct { FilterUDID []string } -type GetBlueprintsOption struct { - FilterName string -} - type ListAppsOption struct { FilterName []string `json:"filter_name"` } @@ -47,7 +41,6 @@ type Service interface { ListDevices(ctx context.Context, opt ListDevicesOption) ([]DeviceDTO, error) ListUsers(ctx context.Context, opt ListUsersOption) ([]user.User, error) GetDEPTokens(ctx context.Context) ([]deptoken.DEPToken, []byte, error) - GetBlueprints(ctx context.Context, opt GetBlueprintsOption) ([]blueprint.Blueprint, error) ListApplications(ctx context.Context, opt ListAppsOption) ([]AppDTO, error) DEPService } @@ -56,12 +49,10 @@ type ListService struct { mtx sync.RWMutex DEPClient dep.Client - Devices *device.DB - Blueprints *blueprint.DB - Profiles profile.Store - Tokens *deptoken.DB - Apps appstore.AppStore - Users *user.DB + Devices *device.DB + Tokens *deptoken.DB + Apps appstore.AppStore + Users *user.DB } func (svc *ListService) ListApplications(ctx context.Context, opts ListAppsOption) ([]AppDTO, error) { @@ -155,19 +146,3 @@ func (svc *ListService) GetDEPTokens(ctx context.Context) ([]deptoken.DEPToken, return tokens, certBytes, nil } - -func (svc *ListService) GetBlueprints(ctx context.Context, opt GetBlueprintsOption) ([]blueprint.Blueprint, error) { - if opt.FilterName != "" { - bp, err := svc.Blueprints.BlueprintByName(opt.FilterName) - if err != nil { - return nil, err - } - return []blueprint.Blueprint{*bp}, err - } else { - bps, err := svc.Blueprints.List() - if err != nil { - return nil, err - } - return bps, nil - } -} diff --git a/platform/api/server/list/transport_http.go b/platform/api/server/list/transport_http.go index 6903e569..f30b2812 100644 --- a/platform/api/server/list/transport_http.go +++ b/platform/api/server/list/transport_http.go @@ -14,7 +14,6 @@ import ( type HTTPHandlers struct { ListDevicesHandler http.Handler GetDEPTokensHandler http.Handler - GetBlueprintsHandler http.Handler GetDEPAccountInfoHandler http.Handler GetDEPProfileHandler http.Handler GetDEPDeviceDetailsHandler http.Handler @@ -35,11 +34,6 @@ func MakeHTTPHandlers(ctx context.Context, endpoints Endpoints, opts ...httptran decodeGetDEPTokensRequest, encodeResponse, opts...), - GetBlueprintsHandler: httptransport.NewServer( - endpoints.GetBlueprintsEndpoint, - decodeGetBlueprintsRequest, - encodeResponse, - opts...), GetDEPAccountInfoHandler: httptransport.NewServer( endpoints.GetDEPAccountInfoEndpoint, decodeDepAccountInfoRequest, @@ -90,17 +84,6 @@ func decodeListUsersRequest(ctx context.Context, r *http.Request) (interface{}, return req, nil } -func decodeGetBlueprintsRequest(ctx context.Context, r *http.Request) (interface{}, error) { - var opts GetBlueprintsOption - if err := json.NewDecoder(r.Body).Decode(&opts); err != nil { - return nil, err - } - req := blueprintsRequest{ - Opts: opts, - } - return req, nil -} - func decodeDepAccountInfoRequest(ctx context.Context, r *http.Request) (interface{}, error) { return nil, nil } @@ -192,15 +175,6 @@ func DecodeGetDEPTokensResponse(_ context.Context, r *http.Response) (interface{ return resp, err } -func DecodeGetBlueprintsResponse(_ context.Context, r *http.Response) (interface{}, error) { - if r.StatusCode != http.StatusOK { - return nil, errorDecoder(r) - } - var resp blueprintsResponse - err := json.NewDecoder(r.Body).Decode(&resp) - return resp, err -} - func DecodeDEPAccountInfoResponse(_ context.Context, r *http.Response) (interface{}, error) { if r.StatusCode != http.StatusOK { return nil, errorDecoder(r) diff --git a/platform/api/server/remove/client.go b/platform/api/server/remove/client.go index 756e16c4..c8c389a5 100644 --- a/platform/api/server/remove/client.go +++ b/platform/api/server/remove/client.go @@ -16,17 +16,6 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra return nil, err } - var removeBlueprintsEndpoint endpoint.Endpoint - { - removeBlueprintsEndpoint = httptransport.NewClient( - "DELETE", - copyURL(u, "/v1/blueprints"), - encodeRequestWithToken(token, EncodeHTTPGenericRequest), - DecodeBlueprintResponse, - opts..., - ).Endpoint() - } - var unblockDeviceEndpoint endpoint.Endpoint { unblockDeviceEndpoint = httptransport.NewClient( @@ -39,8 +28,7 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra } return Endpoints{ - RemoveBlueprintsEndpoint: removeBlueprintsEndpoint, - UnblockDeviceEndpoint: unblockDeviceEndpoint, + UnblockDeviceEndpoint: unblockDeviceEndpoint, }, nil } diff --git a/platform/api/server/remove/endpoint.go b/platform/api/server/remove/endpoint.go index 57e259b9..383b014a 100644 --- a/platform/api/server/remove/endpoint.go +++ b/platform/api/server/remove/endpoint.go @@ -7,14 +7,12 @@ import ( ) type Endpoints struct { - RemoveBlueprintsEndpoint endpoint.Endpoint - UnblockDeviceEndpoint endpoint.Endpoint + UnblockDeviceEndpoint endpoint.Endpoint } func MakeEndpoints(svc Service) Endpoints { e := Endpoints{ - RemoveBlueprintsEndpoint: MakeRemoveBlueprintsEndpoint(svc), - UnblockDeviceEndpoint: MakeUnblockDeviceEndpoint(svc), + UnblockDeviceEndpoint: MakeUnblockDeviceEndpoint(svc), } return e } @@ -28,25 +26,6 @@ func (e Endpoints) UnblockDevice(ctx context.Context, udid string) error { return resp.(unblockDeviceResponse).Err } -func (e Endpoints) RemoveBlueprints(ctx context.Context, names []string) error { - request := blueprintRequest{Names: names} - resp, err := e.RemoveBlueprintsEndpoint(ctx, request) - if err != nil { - return err - } - return resp.(blueprintResponse).Err -} - -func MakeRemoveBlueprintsEndpoint(svc Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(blueprintRequest) - err = svc.RemoveBlueprints(ctx, req.Names) - return blueprintResponse{ - Err: err, - }, nil - } -} - func MakeUnblockDeviceEndpoint(svc Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(unblockDeviceRequest) diff --git a/platform/api/server/remove/service.go b/platform/api/server/remove/service.go index d551b42d..b0db3193 100644 --- a/platform/api/server/remove/service.go +++ b/platform/api/server/remove/service.go @@ -3,28 +3,13 @@ package remove import ( "context" - "github.com/micromdm/micromdm/platform/blueprint" "github.com/micromdm/micromdm/platform/remove" ) type Service interface { - RemoveBlueprints(ctx context.Context, names []string) error UnblockDevice(ctx context.Context, udid string) error } type RemoveService struct { - Blueprints *blueprint.DB *remove.RemoveService } - -func (svc *RemoveService) RemoveBlueprints(ctx context.Context, names []string) error { - // TODO: Wrap deletion(s) in transactions so as to not have - // incomplete removals? - for _, name := range names { - err := svc.Blueprints.Delete(name) - if err != nil { - return err - } - } - return nil -} diff --git a/platform/api/server/remove/transport_http.go b/platform/api/server/remove/transport_http.go index fc5a7a41..58054b79 100644 --- a/platform/api/server/remove/transport_http.go +++ b/platform/api/server/remove/transport_http.go @@ -14,19 +14,11 @@ import ( ) type HTTPHandlers struct { - BlueprintHandler http.Handler - ProfileHandler http.Handler UnblockDeviceHandler http.Handler } func MakeHTTPHandlers(ctx context.Context, endpoint Endpoints, opts ...httptransport.ServerOption) HTTPHandlers { h := HTTPHandlers{ - BlueprintHandler: httptransport.NewServer( - endpoint.RemoveBlueprintsEndpoint, - decodeBlueprintRequest, - encodeResponse, - opts..., - ), UnblockDeviceHandler: httptransport.NewServer( endpoint.UnblockDeviceEndpoint, decodeUnblockDeviceRequest, @@ -37,22 +29,6 @@ func MakeHTTPHandlers(ctx context.Context, endpoint Endpoints, opts ...httptrans return h } -func decodeBlueprintRequest(ctx context.Context, r *http.Request) (interface{}, error) { - var bpReq blueprintRequest - if err := json.NewDecoder(r.Body).Decode(&bpReq); err != nil { - return nil, err - } - return bpReq, nil -} - -func decodeProfileRequest(ctx context.Context, r *http.Request) (interface{}, error) { - var req profileRequest - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - return nil, err - } - return req, nil -} - func decodeUnblockDeviceRequest(ctx context.Context, r *http.Request) (interface{}, error) { var errBadRoute = errors.New("bad route") var req unblockDeviceRequest @@ -117,24 +93,6 @@ func encodeUnblockDeviceRequest(_ context.Context, r *http.Request, request inte return nil } -func DecodeBlueprintResponse(_ context.Context, r *http.Response) (interface{}, error) { - if r.StatusCode != http.StatusOK { - return nil, errorDecoder(r) - } - var resp blueprintResponse - err := json.NewDecoder(r.Body).Decode(&resp) - return resp, err -} - -func DecodeProfileResponse(_ context.Context, r *http.Response) (interface{}, error) { - if r.StatusCode != http.StatusOK { - return nil, errorDecoder(r) - } - var resp profileResponse - err := json.NewDecoder(r.Body).Decode(&resp) - return resp, err -} - func DecodeUnblockDeviceResponse(_ context.Context, r *http.Response) (interface{}, error) { if r.StatusCode != http.StatusOK { return nil, errorDecoder(r) diff --git a/platform/blueprint/apply_blueprint.go b/platform/blueprint/apply_blueprint.go new file mode 100644 index 00000000..c47307d3 --- /dev/null +++ b/platform/blueprint/apply_blueprint.go @@ -0,0 +1,59 @@ +package blueprint + +import ( + "context" + "encoding/json" + "net/http" + + "github.com/go-kit/kit/endpoint" +) + +func (svc *BlueprintService) ApplyBlueprint(ctx context.Context, bp *Blueprint) error { + return svc.store.Save(bp) +} + +type applyBlueprintRequest struct { + Blueprint *Blueprint `json:"blueprint"` +} + +type applyBlueprintResponse struct { + Err error `json:"err,omitempty"` +} + +func (r applyBlueprintResponse) error() error { return r.Err } + +func decodeApplyBlueprintRequest(ctx context.Context, r *http.Request) (interface{}, error) { + var bpReq applyBlueprintRequest + if err := json.NewDecoder(r.Body).Decode(&bpReq); err != nil { + return nil, err + } + return bpReq, nil +} + +func decodeApplyBlueprintResponse(_ context.Context, r *http.Response) (interface{}, error) { + if r.StatusCode != http.StatusOK { + return nil, errorDecoder(r) + } + var resp applyBlueprintResponse + err := json.NewDecoder(r.Body).Decode(&resp) + return resp, err +} + +func MakeApplyBlueprintEndpoint(svc Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (response interface{}, err error) { + req := request.(applyBlueprintRequest) + err = svc.ApplyBlueprint(ctx, req.Blueprint) + return applyBlueprintResponse{ + Err: err, + }, nil + } +} + +func (e Endpoints) ApplyBlueprint(ctx context.Context, bp *Blueprint) error { + request := applyBlueprintRequest{Blueprint: bp} + resp, err := e.ApplyBlueprintEndpoint(ctx, request) + if err != nil { + return err + } + return resp.(applyBlueprintResponse).Err +} diff --git a/platform/blueprint/db.go b/platform/blueprint/builtin/db.go similarity index 85% rename from platform/blueprint/db.go rename to platform/blueprint/builtin/db.go index cfe3234b..342e71d2 100644 --- a/platform/blueprint/db.go +++ b/platform/blueprint/builtin/db.go @@ -1,4 +1,4 @@ -package blueprint +package builtin import ( "fmt" @@ -7,6 +7,7 @@ import ( "github.com/boltdb/bolt" "github.com/pkg/errors" + "github.com/micromdm/micromdm/platform/blueprint" "github.com/micromdm/micromdm/platform/profile" "github.com/micromdm/micromdm/platform/user" ) @@ -46,7 +47,7 @@ func NewDB( return datastore, nil } -func (db *DB) Create(bp *Blueprint) error { +func (db *DB) Create(bp *blueprint.Blueprint) error { _, err := db.BlueprintByName(bp.Name) if err != nil && isNotFound(err) { return errors.New("blueprint must have a unique name") @@ -57,15 +58,15 @@ func (db *DB) Create(bp *Blueprint) error { return db.Save(bp) } -func (db *DB) List() ([]Blueprint, error) { +func (db *DB) List() ([]blueprint.Blueprint, error) { // TODO add filter/limit with ForEach - var blueprints []Blueprint + var blueprints []blueprint.Blueprint err := db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(BlueprintBucket)) c := b.Cursor() for k, v := c.First(); k != nil; k, v = c.Next() { - var bp Blueprint - if err := UnmarshalBlueprint(v, &bp); err != nil { + var bp blueprint.Blueprint + if err := blueprint.UnmarshalBlueprint(v, &bp); err != nil { return err } blueprints = append(blueprints, bp) @@ -75,7 +76,7 @@ func (db *DB) List() ([]Blueprint, error) { return blueprints, err } -func (db *DB) Save(bp *Blueprint) error { +func (db *DB) Save(bp *blueprint.Blueprint) error { err := bp.Verify() if err != nil { return err @@ -97,7 +98,7 @@ func (db *DB) Save(bp *Blueprint) error { if bkt == nil { return fmt.Errorf("bucket %q not found!", BlueprintBucket) } - bpproto, err := MarshalBlueprint(bp) + bpproto, err := blueprint.MarshalBlueprint(bp) if err != nil { return errors.Wrap(err, "marshalling blueprint") } @@ -123,8 +124,8 @@ func (db *DB) Save(bp *Blueprint) error { return tx.Commit() } -func (db *DB) BlueprintByName(name string) (*Blueprint, error) { - var bp Blueprint +func (db *DB) BlueprintByName(name string) (*blueprint.Blueprint, error) { + var bp blueprint.Blueprint err := db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(BlueprintBucket)) ib := tx.Bucket([]byte(blueprintIndexBucket)) @@ -136,7 +137,7 @@ func (db *DB) BlueprintByName(name string) (*Blueprint, error) { if idx == nil { return ¬Found{"Blueprint", fmt.Sprintf("uuid %s", string(idx))} } - return UnmarshalBlueprint(v, &bp) + return blueprint.UnmarshalBlueprint(v, &bp) }) if err != nil { return nil, err @@ -144,8 +145,8 @@ func (db *DB) BlueprintByName(name string) (*Blueprint, error) { return &bp, nil } -func (db *DB) BlueprintsByApplyAt(name string) ([]*Blueprint, error) { - var bps []*Blueprint +func (db *DB) BlueprintsByApplyAt(name string) ([]*blueprint.Blueprint, error) { + var bps []*blueprint.Blueprint err := db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(BlueprintBucket)) c := b.Cursor() @@ -153,8 +154,8 @@ func (db *DB) BlueprintsByApplyAt(name string) ([]*Blueprint, error) { // an array of Blueprints or other more efficient means. Looping // over every blueprint is quite inefficient! for k, v := c.First(); k != nil; k, v = c.Next() { - var bp Blueprint - err := UnmarshalBlueprint(v, &bp) + var bp blueprint.Blueprint + err := blueprint.UnmarshalBlueprint(v, &bp) if err != nil { fmt.Println("could not Unmarshal Blueprint") continue diff --git a/platform/blueprint/listener.go b/platform/blueprint/builtin/listener.go similarity index 95% rename from platform/blueprint/listener.go rename to platform/blueprint/builtin/listener.go index 73d911a9..c1da7fb7 100644 --- a/platform/blueprint/listener.go +++ b/platform/blueprint/builtin/listener.go @@ -1,4 +1,4 @@ -package blueprint +package builtin import ( "context" @@ -8,13 +8,14 @@ import ( "github.com/pkg/errors" "github.com/micromdm/micromdm/mdm/checkin" + "github.com/micromdm/micromdm/platform/blueprint" "github.com/micromdm/micromdm/platform/command" "github.com/micromdm/micromdm/platform/device" "github.com/micromdm/micromdm/platform/profile" "github.com/micromdm/micromdm/platform/pubsub" ) -func (db *DB) ApplyToDevice(ctx context.Context, svc command.Service, bp *Blueprint, udid string) error { +func (db *DB) ApplyToDevice(ctx context.Context, svc command.Service, bp *blueprint.Blueprint, udid string) error { var requests []*mdm.CommandRequest for _, uuid := range bp.UserUUID { fmt.Println("Adding user to admin account") @@ -107,7 +108,7 @@ func (db *DB) StartListener(sub pubsub.Subscriber, cmdSvc command.Service) error // skip UserID token updates continue } - bps, err := db.BlueprintsByApplyAt(ApplyAtEnroll) + bps, err := db.BlueprintsByApplyAt(blueprint.ApplyAtEnroll) if err != nil { fmt.Println(err) continue diff --git a/platform/blueprint/client.go b/platform/blueprint/client.go new file mode 100644 index 00000000..051577cc --- /dev/null +++ b/platform/blueprint/client.go @@ -0,0 +1,70 @@ +package blueprint + +import ( + "context" + "net/http" + "net/url" + + "github.com/go-kit/kit/endpoint" + "github.com/go-kit/kit/log" + httptransport "github.com/go-kit/kit/transport/http" +) + +func NewHTTPClient(instance, token string, logger log.Logger, opts ...httptransport.ClientOption) (Service, error) { + u, err := url.Parse(instance) + if err != nil { + return nil, err + } + + var applyBlueprintEndpoint endpoint.Endpoint + { + applyBlueprintEndpoint = httptransport.NewClient( + "PUT", + copyURL(u, "/v1/blueprints"), + encodeRequestWithToken(token, httptransport.EncodeJSONRequest), + decodeApplyBlueprintResponse, + opts..., + ).Endpoint() + } + + var getBlueprintsEndpoint endpoint.Endpoint + { + getBlueprintsEndpoint = httptransport.NewClient( + "GET", + copyURL(u, "/v1/blueprints"), + encodeRequestWithToken(token, httptransport.EncodeJSONRequest), + decodeGetBlueprintsResponse, + opts..., + ).Endpoint() + } + + var removeBlueprintsEndpoint endpoint.Endpoint + { + removeBlueprintsEndpoint = httptransport.NewClient( + "DELETE", + copyURL(u, "/v1/blueprints"), + encodeRequestWithToken(token, httptransport.EncodeJSONRequest), + decodeRemoveBlueprintsResponse, + opts..., + ).Endpoint() + } + + return Endpoints{ + ApplyBlueprintEndpoint: applyBlueprintEndpoint, + GetBlueprintsEndpoint: getBlueprintsEndpoint, + RemoveBlueprintsEndpoint: removeBlueprintsEndpoint, + }, nil +} + +func encodeRequestWithToken(token string, next httptransport.EncodeRequestFunc) httptransport.EncodeRequestFunc { + return func(ctx context.Context, r *http.Request, request interface{}) error { + r.SetBasicAuth("micromdm", token) + return next(ctx, r, request) + } +} + +func copyURL(base *url.URL, path string) *url.URL { + next := *base + next.Path = path + return &next +} diff --git a/platform/blueprint/get_bplueprints.go b/platform/blueprint/get_bplueprints.go new file mode 100644 index 00000000..f70702fd --- /dev/null +++ b/platform/blueprint/get_bplueprints.go @@ -0,0 +1,73 @@ +package blueprint + +import ( + "context" + "encoding/json" + "net/http" + + "github.com/go-kit/kit/endpoint" +) + +func (svc *BlueprintService) GetBlueprints(ctx context.Context, opt GetBlueprintsOption) ([]Blueprint, error) { + if opt.FilterName != "" { + bp, err := svc.store.BlueprintByName(opt.FilterName) + if err != nil { + return nil, err + } + return []Blueprint{*bp}, err + } else { + bps, err := svc.store.List() + if err != nil { + return nil, err + } + return bps, nil + } +} + +type getBlueprintsRequest struct{ Opts GetBlueprintsOption } +type getBlueprintsResponse struct { + Blueprints []Blueprint `json:"blueprints"` + Err error `json:"err,omitempty"` +} + +func (r getBlueprintsResponse) error() error { return r.Err } + +func decodeGetBlueprintsRequest(ctx context.Context, r *http.Request) (interface{}, error) { + var opts GetBlueprintsOption + if err := json.NewDecoder(r.Body).Decode(&opts); err != nil { + return nil, err + } + req := getBlueprintsRequest{ + Opts: opts, + } + return req, nil +} + +func decodeGetBlueprintsResponse(_ context.Context, r *http.Response) (interface{}, error) { + if r.StatusCode != http.StatusOK { + return nil, errorDecoder(r) + } + var resp getBlueprintsResponse + err := json.NewDecoder(r.Body).Decode(&resp) + return resp, err +} + +func MakeGetBlueprintsEndpoint(svc Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (response interface{}, err error) { + req := request.(getBlueprintsRequest) + blueprints, err := svc.GetBlueprints(ctx, req.Opts) + return getBlueprintsResponse{ + Blueprints: blueprints, + Err: err, + }, nil + } +} + +func (e Endpoints) GetBlueprints(ctx context.Context, opt GetBlueprintsOption) ([]Blueprint, error) { + request := getBlueprintsRequest{opt} + response, err := e.GetBlueprintsEndpoint(ctx, request.Opts) + if err != nil { + return nil, err + } + return response.(getBlueprintsResponse).Blueprints, response.(getBlueprintsResponse).Err +} diff --git a/platform/blueprint/remove_blueprints.go b/platform/blueprint/remove_blueprints.go new file mode 100644 index 00000000..d7f9c55d --- /dev/null +++ b/platform/blueprint/remove_blueprints.go @@ -0,0 +1,65 @@ +package blueprint + +import ( + "context" + "encoding/json" + "net/http" + + "github.com/go-kit/kit/endpoint" +) + +func (svc *BlueprintService) RemoveBlueprints(ctx context.Context, names []string) error { + for _, name := range names { + err := svc.store.Delete(name) + if err != nil { + return err + } + } + return nil +} + +type removeBlueprintsRequest struct { + Names []string `json:"names"` +} + +type removeBlueprintsResponse struct { + Err error `json:"err,omitempty"` +} + +func (r removeBlueprintsResponse) error() error { return r.Err } + +func decodeRemoveBlueprintsRequest(ctx context.Context, r *http.Request) (interface{}, error) { + var req removeBlueprintsRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + return nil, err + } + return req, nil +} + +func decodeRemoveBlueprintsResponse(_ context.Context, r *http.Response) (interface{}, error) { + if r.StatusCode != http.StatusOK { + return nil, errorDecoder(r) + } + var resp removeBlueprintsResponse + err := json.NewDecoder(r.Body).Decode(&resp) + return resp, err +} + +func MakeRemoveBlueprintsEndpoint(svc Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (response interface{}, err error) { + req := request.(removeBlueprintsRequest) + err = svc.RemoveBlueprints(ctx, req.Names) + return removeBlueprintsResponse{ + Err: err, + }, nil + } +} + +func (e Endpoints) RemoveBlueprints(ctx context.Context, names []string) error { + request := removeBlueprintsRequest{Names: names} + resp, err := e.RemoveBlueprintsEndpoint(ctx, request) + if err != nil { + return err + } + return resp.(removeBlueprintsResponse).Err +} diff --git a/platform/blueprint/server.go b/platform/blueprint/server.go new file mode 100644 index 00000000..0248f4cd --- /dev/null +++ b/platform/blueprint/server.go @@ -0,0 +1,76 @@ +package blueprint + +import ( + "encoding/json" + "net/http" + + "github.com/go-kit/kit/endpoint" + "github.com/go-kit/kit/log" + httptransport "github.com/go-kit/kit/transport/http" + "github.com/gorilla/mux" + "github.com/pkg/errors" +) + +type Endpoints struct { + ApplyBlueprintEndpoint endpoint.Endpoint + GetBlueprintsEndpoint endpoint.Endpoint + RemoveBlueprintsEndpoint endpoint.Endpoint +} + +func MakeServerEndpoints(s Service) Endpoints { + return Endpoints{ + GetBlueprintsEndpoint: MakeGetBlueprintsEndpoint(s), + ApplyBlueprintEndpoint: MakeApplyBlueprintEndpoint(s), + } +} + +func MakeHTTPHandler(e Endpoints, logger log.Logger) http.Handler { + options := []httptransport.ServerOption{ + httptransport.ServerErrorLogger(logger), + } + + r := mux.NewRouter() + + // PUT /v1/blueprints create or replace a blueprint on the server + // GET /v1/blueprints get a list of blueprints managed by the server + // DELETE /v1/blueprints remove one or more blueprints from the server + + r.Methods("PUT").Path("/v1/blueprints").Handler(httptransport.NewServer( + e.ApplyBlueprintEndpoint, + decodeApplyBlueprintRequest, + httptransport.EncodeJSONResponse, + options..., + )) + + r.Methods("GET").Path("/v1/blueprints").Handler(httptransport.NewServer( + e.GetBlueprintsEndpoint, + decodeGetBlueprintsRequest, + httptransport.EncodeJSONResponse, + options..., + )) + + r.Methods("DELETE").Path("/v1/blueprints").Handler(httptransport.NewServer( + e.RemoveBlueprintsEndpoint, + decodeRemoveBlueprintsRequest, + httptransport.EncodeJSONResponse, + options..., + )) + + return r +} + +type errorWrapper struct { + Error string `json:"error"` +} + +type errorer interface { + error() error +} + +func errorDecoder(r *http.Response) error { + var w errorWrapper + if err := json.NewDecoder(r.Body).Decode(&w); err != nil { + return err + } + return errors.New(w.Error) +} diff --git a/platform/blueprint/service.go b/platform/blueprint/service.go new file mode 100644 index 00000000..f16c61f5 --- /dev/null +++ b/platform/blueprint/service.go @@ -0,0 +1,30 @@ +package blueprint + +import ( + "context" +) + +type GetBlueprintsOption struct { + FilterName string +} + +type Service interface { + ApplyBlueprint(ctx context.Context, bp *Blueprint) error + GetBlueprints(ctx context.Context, opt GetBlueprintsOption) ([]Blueprint, error) + RemoveBlueprints(ctx context.Context, names []string) error +} + +type Store interface { + Save(*Blueprint) error + BlueprintByName(name string) (*Blueprint, error) + List() ([]Blueprint, error) + Delete(string) error +} + +type BlueprintService struct { + store Store +} + +func New(store Store) *BlueprintService { + return &BlueprintService{store: store} +}