Skip to content

Commit

Permalink
Allow refreshing a single service. Fixes #462
Browse files Browse the repository at this point in the history
  • Loading branch information
plorenz committed Dec 11, 2023
1 parent 34437ca commit 20a65d2
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 33 deletions.
21 changes: 21 additions & 0 deletions ziti/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,27 @@ func (self *CtrlClient) GetServices() ([]*rest_model.ServiceDetail, error) {
return services, nil
}

// GetService will fetch the specific service requested. If the service doesn't exist,
// nil will be returned
func (self *CtrlClient) GetService(name string) (*rest_model.ServiceDetail, error) {
params := service.NewListServicesParams()

filter := fmt.Sprintf(`name="%s"`, name)
params.Filter = &filter

resp, err := self.API.Service.ListServices(params, nil)

if err != nil {
return nil, rest_util.WrapErr(err)
}

if len(resp.Payload.Data) > 0 {
return resp.Payload.Data[0], nil
}

return nil, nil
}

// GetServiceTerminators returns the client terminator details for a specific service.
func (self *CtrlClient) GetServiceTerminators(svc *rest_model.ServiceDetail, offset int, limit int) ([]*rest_model.TerminatorClientDetail, int, error) {
params := service.NewListServiceTerminatorsParams()
Expand Down
138 changes: 105 additions & 33 deletions ziti/ziti.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ type Context interface {
// to.
RefreshServices() error

// RefreshService forces the context to refresh just the service with the given name. If the given service isn't
// found, a nil will be returned
RefreshService(serviceName string) (*rest_model.ServiceDetail, error)

// GetServiceTerminators will return a slice of rest_model.TerminatorClientDetail for a specific service name.
// The offset and limit options can be used to page through excessive lists of items. A max of 500 is imposed on
// limit.
Expand Down Expand Up @@ -480,53 +484,88 @@ func (context *ContextImpl) processServiceUpdates(services []*rest_model.Service

// Adds and Updates
for _, s := range services {
isChange := false
valuesDiffer := false
context.processServiceAddOrUpdated(s)
}

_ = context.services.Upsert(*s.Name, s, func(exist bool, valueInMap *rest_model.ServiceDetail, newValue *rest_model.ServiceDetail) *rest_model.ServiceDetail {
isChange = exist
if isChange {
valuesDiffer = !reflect.DeepEqual(newValue, valueInMap)
}
context.refreshServiceQueryMap()
}

return newValue
func (context *ContextImpl) processSingleServiceUpdate(name string, s *rest_model.ServiceDetail) {
// process Deletes
if s == nil {
var deletes []string
context.services.IterCb(func(key string, svc *rest_model.ServiceDetail) {
if *svc.Name == name {
deletes = append(deletes, key)
if context.options.OnServiceUpdate != nil {
context.options.OnServiceUpdate(ServiceRemoved, svc)
}
context.Emit(EventServiceRemoved, svc)
context.deleteServiceSessions(*svc.ID)
}
})

for _, deletedKey := range deletes {
context.services.Remove(deletedKey)
context.intercepts.Remove(deletedKey)
}
} else {
// Adds and Updates
context.processServiceAddOrUpdated(s)
}

context.refreshServiceQueryMap()
}

func (context *ContextImpl) processServiceAddOrUpdated(s *rest_model.ServiceDetail) {
isChange := false
valuesDiffer := false

_ = context.services.Upsert(*s.Name, s, func(exist bool, valueInMap *rest_model.ServiceDetail, newValue *rest_model.ServiceDetail) *rest_model.ServiceDetail {
isChange = exist
if isChange {
context.Emit(EventServiceChanged, s)
} else {
context.Emit(EventServiceAdded, s)
valuesDiffer = !reflect.DeepEqual(newValue, valueInMap)
}

if context.options.OnServiceUpdate != nil {
if isChange {
if valuesDiffer {
context.options.OnServiceUpdate(ServiceChanged, s)
}
} else {
context.services.Set(*s.Name, s)
context.options.OnServiceUpdate(ServiceAdded, s)
return newValue
})

if isChange {
context.Emit(EventServiceChanged, s)
} else {
context.Emit(EventServiceAdded, s)
}

if context.options.OnServiceUpdate != nil {
if isChange {
if valuesDiffer {
context.options.OnServiceUpdate(ServiceChanged, s)
}
} else {
context.services.Set(*s.Name, s)
context.options.OnServiceUpdate(ServiceAdded, s)
}
}

intercept := &edge.InterceptV1Config{}
ok, err := edge.ParseServiceConfig(s, InterceptV1, intercept)
if err != nil {
pfxlog.Logger().Warnf("failed to parse config[%s] for service[%s]", InterceptV1, *s.Name)
} else if ok {
intercept := &edge.InterceptV1Config{}
ok, err := edge.ParseServiceConfig(s, InterceptV1, intercept)
if err != nil {
pfxlog.Logger().Warnf("failed to parse config[%s] for service[%s]", InterceptV1, *s.Name)
} else if ok {
intercept.Service = s
context.intercepts.Set(*s.Name, intercept)
} else {
cltCfg := &edge.ClientConfig{}
ok, err := edge.ParseServiceConfig(s, ClientConfigV1, cltCfg)
if err == nil && ok {
intercept = cltCfg.ToInterceptV1Config()
intercept.Service = s
context.intercepts.Set(*s.Name, intercept)
} else {
cltCfg := &edge.ClientConfig{}
ok, err := edge.ParseServiceConfig(s, ClientConfigV1, cltCfg)
if err == nil && ok {
intercept = cltCfg.ToInterceptV1Config()
intercept.Service = s
context.intercepts.Set(*s.Name, intercept)
}
}
}
}

func (context *ContextImpl) refreshServiceQueryMap() {
serviceQueryMap := map[string]map[string]rest_model.PostureQuery{} //serviceId -> queryId -> query

context.services.IterCb(func(key string, svc *rest_model.ServiceDetail) {
Expand Down Expand Up @@ -618,7 +657,7 @@ func (context *ContextImpl) refreshServices(forceCheck bool) error {
if errors.As(err, &target) {
log.Info("attempting to re-authenticate")
if authErr := context.Authenticate(); authErr != nil {
log.WithError(authErr).Error("unable to re-authenticate during session refresh")
log.WithError(authErr).Error("unable to re-authenticate during services refresh")
return err
}
if services, err = context.CtrlClt.GetServices(); err != nil {
Expand All @@ -636,6 +675,39 @@ func (context *ContextImpl) refreshServices(forceCheck bool) error {
return nil
}

func (context *ContextImpl) RefreshService(serviceName string) (*rest_model.ServiceDetail, error) {
if err := context.ensureApiSession(); err != nil {
return nil, fmt.Errorf("failed to refresh service: %v", err)
}

var err error

log := pfxlog.Logger().WithField("serviceName", serviceName)

log.Debug("refreshing service")

serviceDetail, err := context.CtrlClt.GetService(serviceName)
if err != nil {
target := &service.ListServicesUnauthorized{}
if errors.As(err, &target) {
log.Info("attempting to re-authenticate")
if authErr := context.Authenticate(); authErr != nil {
log.WithError(authErr).Error("unable to re-authenticate during service refresh")
return nil, err
}
if serviceDetail, err = context.CtrlClt.GetService(serviceName); err != nil {
return nil, err
}
} else {
return nil, err
}
}

context.processSingleServiceUpdate(serviceName, serviceDetail)

return serviceDetail, nil
}

func (context *ContextImpl) runSessionRefresh() {
log := pfxlog.Logger()
svcUpdateTick := time.NewTicker(context.options.RefreshInterval)
Expand Down

0 comments on commit 20a65d2

Please sign in to comment.