Skip to content

Commit

Permalink
fix(api): register service check if already exists based on consumer (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
richardlt authored Jan 24, 2020
1 parent c08ddfe commit 7fbafaa
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 41 deletions.
71 changes: 31 additions & 40 deletions engine/api/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import (
"net/http"
"time"

"github.com/ovh/cds/engine/api/group"

"github.com/go-gorp/gorp"
"github.com/gorilla/mux"

Expand Down Expand Up @@ -35,58 +33,49 @@ func (api *API) getExternalServiceHandler() service.Handler {

func (api *API) postServiceRegisterHandler() service.Handler {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
var srv sdk.Service
if err := service.UnmarshalBody(r, &srv); err != nil {
return sdk.WithStack(err)
}
consumer := getAPIConsumer(ctx)

//Service must be with a sharedinfra group token
// except for hatchery: users can start hatchery with their group
if !isGroupMember(ctx, group.SharedInfraGroup) && srv.Type != services.TypeHatchery {
return sdk.WrapError(sdk.ErrForbidden, "Cannot register service for token %s with service %s", getAPIConsumer(ctx).ID, srv.Type)
var data sdk.Service
if err := service.UnmarshalBody(r, &data); err != nil {
return sdk.WithStack(err)
}
data.LastHeartbeat = time.Now()

// For hatcheries, the user who created the used token must be admin of all groups in the token
if srv.Type == services.TypeHatchery && !isAdmin(ctx) {
links, err := group.LoadLinksGroupUserForUserIDs(ctx, api.mustDB(), []int64{getAPIConsumer(ctx).AuthentifiedUser.OldUserStruct.ID})
if err != nil {
return err
}
groupsAdminIDs := links.ToGroupIDs()
for _, gID := range getAPIConsumer(ctx).GetGroupIDs() {
if !sdk.IsInInt64Array(gID, groupsAdminIDs) {
return sdk.WrapError(sdk.ErrForbidden, "Cannot register service for token %s with service %s", getAPIConsumer(ctx).ID, srv.Type)
}
}
// Service that are not hatcheries should be started be an admin
if data.Type != services.TypeHatchery && !isAdmin(ctx) {
return sdk.WrapError(sdk.ErrForbidden, "cannot register service of type %s for consumer %s", data.Type, consumer.ID)
}

srv.Uptodate = srv.Version == sdk.VERSION
srv.ConsumerID = &getAPIConsumer(ctx).ID
srv.LastHeartbeat = time.Now()

//Insert or update the service
// Insert or update the service
tx, err := api.mustDB().Begin()
if err != nil {
return sdk.WrapError(err, "Cannot start transaction")
return sdk.WithStack(err)
}
defer tx.Rollback() // nolint

//Try to find the service, and keep; else generate a new one
oldSrv, errOldSrv := services.LoadByName(ctx, tx, srv.Name)
if oldSrv != nil {
srv.ID = oldSrv.ID
if err := services.Update(ctx, tx, &srv); err != nil {
return sdk.WithStack(err)
// Try to find the service, and keep; else generate a new one
srv, err := services.LoadByConsumerID(ctx, tx, consumer.ID)
if err != nil && !sdk.ErrorIs(err, sdk.ErrNotFound) {
return err
}
if srv != nil && !(srv.Type == data.Type) {
return sdk.WrapError(sdk.ErrForbidden, "cannot register service %s of type %s for consumer %s while existing service type is different", data.Name, data.Type, consumer.ID)
}

// Update or create the service
if srv != nil {
srv.Update(data)
if err := services.Update(ctx, tx, srv); err != nil {
return err
}
log.Debug("postServiceRegisterHandler> service %s(%d) registered for consumer %v", srv.Name, srv.ID, *srv.ConsumerID)
} else if !sdk.ErrorIs(errOldSrv, sdk.ErrNotFound) {
log.Error(ctx, "postServiceRegisterHandler> unable to find service by name %s: %v", srv.Name, errOldSrv)
return sdk.WithStack(errOldSrv)
log.Debug("postServiceRegisterHandler> update existing service %s(%d) registered for consumer %s", srv.Name, srv.ID, *srv.ConsumerID)
} else {
if err := services.Insert(ctx, tx, &srv); err != nil {
srv = &data
srv.ConsumerID = &consumer.ID
if err := services.Insert(ctx, tx, srv); err != nil {
return sdk.WithStack(err)
}
log.Debug("postServiceRegisterHandler> new service %s(%d) registered for consumer %v", srv.Name, srv.ID, *srv.ConsumerID)
log.Debug("postServiceRegisterHandler> insert new service %s(%d) registered for consumer %s", srv.Name, srv.ID, *srv.ConsumerID)
}

if len(srv.PublicKey) > 0 {
Expand All @@ -97,6 +86,8 @@ func (api *API) postServiceRegisterHandler() service.Handler {
return sdk.WithStack(err)
}

srv.Uptodate = data.Version == sdk.VERSION

return service.WriteJSON(w, srv, http.StatusOK)
}
}
Expand Down
12 changes: 11 additions & 1 deletion sdk/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type CanonicalService struct {
PublicKey []byte `json:"public_key" db:"public_key"`
}

// Service is a µService registered on CDS API
// Service is a µService registered on CDS API.
type Service struct {
CanonicalService
LastHeartbeat time.Time `json:"last_heartbeat" db:"last_heartbeat" cli:"heartbeat"`
Expand All @@ -26,6 +26,16 @@ type Service struct {
Uptodate bool `json:"up_to_date" db:"-"`
}

// Update service field from new data.
func (s *Service) Update(data Service) {
s.Name = data.Name
s.HTTPURL = data.HTTPURL
s.Config = data.Config
s.PublicKey = data.PublicKey
s.LastHeartbeat = data.LastHeartbeat
s.MonitoringStatus = data.MonitoringStatus
}

type ServiceConfig map[string]interface{}

// Value returns driver.Value from workflow template request.
Expand Down

0 comments on commit 7fbafaa

Please sign in to comment.