github.com/Axway/agent-sdk@v1.1.101/pkg/apic/apiserver/clients/api/v1/fake.go (about)

     1  package v1
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  	"sync"
     8  	"time"
     9  
    10  	apiv1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1"
    11  	"github.com/google/uuid"
    12  )
    13  
    14  func event(eType apiv1.EventType, ri *apiv1.ResourceInstance) *apiv1.Event {
    15  	return &apiv1.Event{
    16  		ID:   uuid.New().String(),
    17  		Type: eType,
    18  		Payload: apiv1.EventPayload{
    19  			GroupKind:  ri.GroupKind,
    20  			Scope:      ri.Metadata.Scope,
    21  			Tags:       ri.Tags,
    22  			Attributes: ri.Attributes,
    23  			ID:         ri.Metadata.ID,
    24  			Name:       ri.Name,
    25  			References: ri.Metadata.References, // needed ?
    26  		},
    27  	}
    28  }
    29  
    30  type fakeGroup map[string]fakeVersion
    31  
    32  type fakeVersion map[string]*fakeUnscoped
    33  
    34  type fakeAttribute struct {
    35  	key   string
    36  	value string
    37  }
    38  
    39  type fakeUnscoped struct {
    40  	fakeScoped
    41  	scopedKinds map[string]fakeByScope
    42  }
    43  
    44  func notFound(name, kind string) NotFoundError {
    45  	return NotFoundError{[]apiv1.Error{{
    46  		Status: 404,
    47  		Title:  "Not found error",
    48  		Detail: fmt.Sprintf("Resource %s of kind %s not found.", name, kind),
    49  	}}}
    50  }
    51  
    52  func notFoundInScope(name, kind, scopeName string) NotFoundError {
    53  	return NotFoundError{[]apiv1.Error{{
    54  		Status: 404,
    55  		Title:  "Not found error",
    56  		Detail: fmt.Sprintf("Resource %s of kind %s not found in scope %s.", name, kind, scopeName),
    57  	}}}
    58  }
    59  
    60  type unknownScope NotFoundError
    61  
    62  // Create -
    63  func (us unknownScope) Create(ri *apiv1.ResourceInstance, opts ...CreateOption) (*apiv1.ResourceInstance, error) {
    64  	return us.CreateCtx(context.Background(), ri)
    65  }
    66  
    67  // CreateCtx -
    68  func (us unknownScope) CreateCtx(_ context.Context, _ *apiv1.ResourceInstance) (*apiv1.ResourceInstance, error) {
    69  	return nil, NotFoundError(us)
    70  }
    71  
    72  // Delete -
    73  func (us unknownScope) Delete(ri *apiv1.ResourceInstance) error {
    74  	return us.DeleteCtx(context.Background(), ri)
    75  }
    76  
    77  // DeleteCtx -
    78  func (us unknownScope) DeleteCtx(ctx context.Context, _ *apiv1.ResourceInstance) error {
    79  	return NotFoundError(us)
    80  }
    81  
    82  // Get -
    83  func (us unknownScope) Get(name string) (*apiv1.ResourceInstance, error) {
    84  	return us.GetCtx(context.Background(), name)
    85  }
    86  
    87  // GetCtx -
    88  func (us unknownScope) GetCtx(_ context.Context, _ string) (*apiv1.ResourceInstance, error) {
    89  	return nil, NotFoundError(us)
    90  }
    91  
    92  // List -
    93  func (us unknownScope) List(options ...ListOptions) ([]*apiv1.ResourceInstance, error) {
    94  	return us.ListCtx(context.Background(), options...)
    95  }
    96  
    97  // ListCtx -
    98  func (us unknownScope) ListCtx(_ context.Context, _ ...ListOptions) ([]*apiv1.ResourceInstance, error) {
    99  	return nil, NotFoundError(us)
   100  }
   101  
   102  // Update -
   103  func (us unknownScope) Update(ri *apiv1.ResourceInstance, opts ...UpdateOption) (*apiv1.ResourceInstance, error) {
   104  	return us.UpdateCtx(context.Background(), ri, opts...)
   105  }
   106  
   107  // UpdateCtx -
   108  func (us unknownScope) UpdateCtx(_ context.Context, _ *apiv1.ResourceInstance, opts ...UpdateOption) (*apiv1.ResourceInstance, error) {
   109  	return nil, NotFoundError(us)
   110  }
   111  
   112  type fakeByScope struct {
   113  	apiv1.GroupVersionKind
   114  	scopeKind apiv1.GroupKind
   115  	fks       map[string]*fakeScoped
   116  }
   117  
   118  // Create -
   119  func (fk fakeByScope) Create(ri *apiv1.ResourceInstance, opts ...CreateOption) (*apiv1.ResourceInstance, error) {
   120  	return fk.CreateCtx(context.Background(), ri, opts...)
   121  }
   122  
   123  // CreateCtx -
   124  func (fk fakeByScope) CreateCtx(c context.Context, ri *apiv1.ResourceInstance, opts ...CreateOption) (*apiv1.ResourceInstance, error) {
   125  	newri := *ri
   126  
   127  	newri.GroupVersionKind = fk.GroupVersionKind
   128  	newri.ResourceMeta.Metadata.Scope.Kind = fk.scopeKind.Kind
   129  
   130  	return fk.fks[ri.ResourceMeta.Metadata.Scope.Name].CreateCtx(c, &newri, opts...)
   131  }
   132  
   133  // Delete -
   134  func (fk fakeByScope) Delete(ri *apiv1.ResourceInstance) error {
   135  	return fk.DeleteCtx(context.Background(), ri)
   136  }
   137  
   138  // DeleteCtx -
   139  func (fk fakeByScope) DeleteCtx(c context.Context, ri *apiv1.ResourceInstance) error {
   140  	newri := *ri
   141  
   142  	newri.GroupVersionKind = fk.GroupVersionKind
   143  	newri.ResourceMeta.Metadata.Scope.Kind = fk.scopeKind.Kind
   144  
   145  	return fk.fks[ri.ResourceMeta.Metadata.Scope.Name].DeleteCtx(c, &newri)
   146  }
   147  
   148  // Get -
   149  func (fk fakeByScope) Get(ri string) (*apiv1.ResourceInstance, error) {
   150  	return fk.GetCtx(context.Background(), ri)
   151  }
   152  
   153  // GetCtx -
   154  func (fk fakeByScope) GetCtx(_ context.Context, name string) (*apiv1.ResourceInstance, error) {
   155  	split := strings.SplitN(name, `/`, 2)
   156  
   157  	if len(split) == 2 {
   158  		return fk.fks[split[0]].Get(split[1])
   159  	}
   160  
   161  	return nil, notFound("", fk.scopeKind.Kind)
   162  }
   163  
   164  // List -
   165  func (fk fakeByScope) List(ri ...ListOptions) ([]*apiv1.ResourceInstance, error) {
   166  	return fk.ListCtx(context.Background(), ri...)
   167  }
   168  
   169  // ListCtx -
   170  func (fk fakeByScope) ListCtx(_ context.Context, options ...ListOptions) ([]*apiv1.ResourceInstance, error) {
   171  	return nil, notFound("", "")
   172  }
   173  
   174  // Update -
   175  func (fk fakeByScope) Update(ri *apiv1.ResourceInstance, opts ...UpdateOption) (*apiv1.ResourceInstance, error) {
   176  	return fk.UpdateCtx(context.Background(), ri, opts...)
   177  }
   178  
   179  // UpdateCtx -
   180  func (fk fakeByScope) UpdateCtx(c context.Context, ri *apiv1.ResourceInstance, opts ...UpdateOption) (*apiv1.ResourceInstance, error) {
   181  	newri := *ri
   182  
   183  	newri.GroupVersionKind = fk.GroupVersionKind
   184  	newri.ResourceMeta.Metadata.Scope.Kind = fk.scopeKind.Kind
   185  
   186  	return fk.fks[ri.ResourceMeta.Metadata.Scope.Name].UpdateCtx(c, &newri, opts...)
   187  }
   188  
   189  // WithScope -
   190  func (fk fakeByScope) WithScope(name string) Scoped {
   191  	if s, ok := fk.fks[name]; !ok {
   192  		return unknownScope(notFound(name, fk.scopeKind.Kind))
   193  	} else {
   194  		return s
   195  	}
   196  }
   197  
   198  // Create -
   199  func (fk *fakeUnscoped) Create(ri *apiv1.ResourceInstance, opts ...CreateOption) (*apiv1.ResourceInstance, error) {
   200  	return fk.CreateCtx(context.Background(), ri, opts...)
   201  }
   202  
   203  // CreateCtx -
   204  func (fk *fakeUnscoped) CreateCtx(_ context.Context, ri *apiv1.ResourceInstance, opts ...CreateOption) (*apiv1.ResourceInstance, error) {
   205  	fk.lock.Lock()
   206  	defer fk.lock.Unlock()
   207  
   208  	return fk.create(ri, opts...)
   209  }
   210  
   211  // Delete -
   212  func (fk *fakeUnscoped) Delete(ri *apiv1.ResourceInstance) error {
   213  	return fk.DeleteCtx(context.Background(), ri)
   214  }
   215  
   216  // DeleteCtx -
   217  func (fk *fakeUnscoped) DeleteCtx(_ context.Context, ri *apiv1.ResourceInstance) error {
   218  	if fk == nil {
   219  		return notFound(ri.Metadata.Scope.Name, ri.Metadata.Scope.Kind)
   220  	}
   221  
   222  	fk.lock.Lock()
   223  	defer fk.lock.Unlock()
   224  
   225  	_, ok := fk.resources[ri.Name]
   226  	if !ok {
   227  		return notFound(ri.Name, fk.Kind)
   228  	}
   229  
   230  	for _, sk := range fk.scopedKinds {
   231  		sk.fks[ri.Name].deleteAll()
   232  
   233  		sk.fks[ri.Name] = nil
   234  	}
   235  
   236  	return fk.fakeScoped.delete(ri)
   237  }
   238  
   239  // Get -
   240  func (fk *fakeUnscoped) Get(ri string) (*apiv1.ResourceInstance, error) {
   241  	return fk.GetCtx(context.Background(), ri)
   242  }
   243  
   244  // GetCtx -
   245  func (fk *fakeUnscoped) GetCtx(_ context.Context, name string) (*apiv1.ResourceInstance, error) {
   246  	fk.lock.Lock()
   247  	defer fk.lock.Unlock()
   248  
   249  	return fk.get(name)
   250  }
   251  
   252  // WithScope -
   253  func (fk *fakeScoped) WithScope(name string) Scoped {
   254  	return (*fakeScoped)(nil)
   255  }
   256  
   257  func (fk *fakeUnscoped) create(ri *apiv1.ResourceInstance, opts ...CreateOption) (*apiv1.ResourceInstance, error) {
   258  	created, err := fk.fakeScoped.create(ri, opts...)
   259  	if err != nil {
   260  		return created, err
   261  	}
   262  
   263  	for kind, scoped := range fk.scopedKinds {
   264  		scoped.fks[created.Name] = newFakeKind(
   265  			apiv1.GroupVersionKind{
   266  				GroupKind: apiv1.GroupKind{
   267  					Kind:  kind,
   268  					Group: fk.Group,
   269  				},
   270  				APIVersion: fk.APIVersion,
   271  			},
   272  			apiv1.MetadataScope{
   273  				ID:   created.Metadata.ID,
   274  				Kind: fk.GroupVersionKind.Kind,
   275  				Name: created.Name,
   276  			},
   277  			fk.handler,
   278  		)
   279  	}
   280  
   281  	return created, nil
   282  }
   283  
   284  // WithScope -
   285  func (fk *fakeUnscoped) WithScope(name string) Scoped {
   286  	return (*fakeScoped)(nil)
   287  }
   288  
   289  type set map[string]struct{}
   290  
   291  func newSet(entries ...string) set {
   292  	s := set{}
   293  	for _, entry := range entries {
   294  		s[entry] = struct{}{}
   295  	}
   296  	return s
   297  }
   298  
   299  // Union -
   300  func (s set) Union(other set) set {
   301  	res := set{}
   302  	for k, v := range s {
   303  		res[k] = v
   304  	}
   305  
   306  	for k, v := range other {
   307  		res[k] = v
   308  	}
   309  
   310  	return res
   311  }
   312  
   313  // Intersection -
   314  func (s set) Intersection(other set) set {
   315  	res := set{}
   316  	for k, v := range s {
   317  		if _, ok := other[k]; ok {
   318  			res[k] = v
   319  		}
   320  	}
   321  
   322  	return res
   323  }
   324  
   325  type index map[string][]string
   326  
   327  // LookUp -
   328  func (idx index) LookUp(key string) set {
   329  	names, ok := idx[key]
   330  	if !ok {
   331  		return set{}
   332  	}
   333  
   334  	return newSet(names...)
   335  }
   336  
   337  // Update -
   338  func (idx index) Update(old []string, new []string, val string) {
   339  	toDelete := append([]string{}, old...)
   340  	toAdd := append([]string{}, new...)
   341  
   342  	n := 0
   343  outer:
   344  	for _, old := range toDelete {
   345  		for j, new := range toAdd {
   346  			if old == new {
   347  				toAdd[j] = toAdd[len(toAdd)-1]
   348  				toAdd = toAdd[:len(toAdd)-1]
   349  				continue outer
   350  			}
   351  		}
   352  		toDelete[n] = old
   353  		n++
   354  	}
   355  
   356  	toDelete = toDelete[:n]
   357  
   358  	for _, del := range toDelete {
   359  		names, ok := idx[del]
   360  		if !ok {
   361  			panic(fmt.Sprintf("Trying to delete unknown index %s", del))
   362  		}
   363  
   364  		for i := range names {
   365  			if names[i] == val {
   366  				names[i] = names[len(names)-1]
   367  				idx[del] = names[:len(names)-1]
   368  				break
   369  			}
   370  		}
   371  	}
   372  
   373  	for _, add := range toAdd {
   374  		names, ok := idx[add]
   375  		if !ok {
   376  			names = []string{}
   377  		}
   378  		idx[add] = append(names, val)
   379  	}
   380  }
   381  
   382  type FakeVisitor struct {
   383  	resources *fakeScoped
   384  	set
   385  }
   386  
   387  // Visit -
   388  func (fv *FakeVisitor) Visit(node QueryNode) {
   389  	switch n := node.(type) {
   390  	case andNode:
   391  		for i, child := range n {
   392  			childFV := &FakeVisitor{fv.resources, set{}}
   393  			child.Accept(childFV)
   394  			if i == 0 {
   395  				fv.set = childFV.set
   396  			} else {
   397  				fv.set = fv.set.Intersection(childFV.set)
   398  			}
   399  		}
   400  	case orNode:
   401  		for _, child := range n {
   402  			childFV := &FakeVisitor{fv.resources, set{}}
   403  			child.Accept(childFV)
   404  			fv.set = fv.set.Union(childFV.set)
   405  		}
   406  	case namesNode:
   407  		fv.set = newSet(n...).Intersection(fv.resources.nameSet())
   408  	case tagNode:
   409  		for _, tag := range n {
   410  			fv.set = fv.set.Union(fv.resources.tagsIndex.LookUp(tag))
   411  		}
   412  	case *attrNode:
   413  		for _, val := range n.values {
   414  			fv.set = fv.set.Union(fv.resources.attributeIndex.LookUp(fmt.Sprintf("%s;%s", n.key, val)))
   415  		}
   416  	default:
   417  		panic(fmt.Sprintf("unknown node type %+v", n))
   418  	}
   419  }
   420  
   421  func attrsAsIdxs(attrs map[string]string) []string {
   422  	// update attributes
   423  	idxs := make([]string, len(attrs))
   424  
   425  	for key, val := range attrs {
   426  		idxs = append(idxs, fmt.Sprintf("%s;%s", key, val))
   427  	}
   428  	return idxs
   429  }
   430  
   431  type fakeScoped struct {
   432  	apiv1.GroupVersionKind
   433  	ms             apiv1.MetadataScope
   434  	resources      map[string]*apiv1.ResourceInstance
   435  	tagsIndex      index
   436  	attributeIndex index
   437  	lock           *sync.Mutex
   438  	handler        EventHandler
   439  }
   440  
   441  func (fk *fakeScoped) nameSet() set {
   442  	res := make(set, len(fk.resources))
   443  	for k := range fk.resources {
   444  		res[k] = struct{}{}
   445  	}
   446  
   447  	return res
   448  }
   449  
   450  // Create -
   451  func (fk *fakeScoped) Create(ri *apiv1.ResourceInstance, opts ...CreateOption) (*apiv1.ResourceInstance, error) {
   452  	return fk.CreateCtx(context.Background(), ri, opts...)
   453  }
   454  
   455  // CreateCtx -
   456  func (fk *fakeScoped) CreateCtx(_ context.Context, ri *apiv1.ResourceInstance, opts ...CreateOption) (*apiv1.ResourceInstance, error) {
   457  	if fk == nil {
   458  		return nil, notFound(ri.Metadata.Scope.Name, ri.Metadata.Scope.Kind)
   459  	}
   460  
   461  	fk.lock.Lock()
   462  	defer fk.lock.Unlock()
   463  
   464  	return fk.create(ri, opts...)
   465  }
   466  
   467  // Delete -
   468  func (fk *fakeScoped) Delete(ri *apiv1.ResourceInstance) error {
   469  	return fk.DeleteCtx(context.Background(), ri)
   470  }
   471  
   472  // DeleteCtx -
   473  func (fk *fakeScoped) DeleteCtx(_ context.Context, ri *apiv1.ResourceInstance) error {
   474  	if fk == nil {
   475  		return notFound(ri.Metadata.Scope.Name, ri.Metadata.Scope.Kind)
   476  	}
   477  
   478  	fk.lock.Lock()
   479  	defer fk.lock.Unlock()
   480  
   481  	return fk.delete(ri)
   482  }
   483  
   484  func (fk *fakeScoped) delete(ri *apiv1.ResourceInstance) error {
   485  	deleted, ok := fk.resources[ri.Name]
   486  	if !ok {
   487  		return notFoundInScope(ri.Name, fk.Kind, fk.ms.Name)
   488  	}
   489  
   490  	fk.attributeIndex.Update(attrsAsIdxs(deleted.Attributes), []string{}, deleted.Name)
   491  	fk.tagsIndex.Update(deleted.Tags, []string{}, deleted.Name)
   492  
   493  	fk.handler.Handle(event(apiv1.ResourceEntryDeletedEvent, deleted))
   494  
   495  	return nil
   496  }
   497  
   498  // Get -
   499  func (fk *fakeScoped) Get(ri string) (*apiv1.ResourceInstance, error) {
   500  	return fk.GetCtx(context.Background(), ri)
   501  }
   502  
   503  // GetCtx -
   504  func (fk *fakeScoped) GetCtx(_ context.Context, name string) (*apiv1.ResourceInstance, error) {
   505  	fk.lock.Lock()
   506  	defer fk.lock.Unlock()
   507  
   508  	return fk.get(name)
   509  }
   510  
   511  // List -
   512  func (fk *fakeScoped) List(ri ...ListOptions) ([]*apiv1.ResourceInstance, error) {
   513  	return fk.ListCtx(context.Background(), ri...)
   514  }
   515  
   516  // ListCtx -
   517  func (fk *fakeScoped) ListCtx(_ context.Context, options ...ListOptions) ([]*apiv1.ResourceInstance, error) {
   518  	if fk == nil {
   519  		return nil, fmt.Errorf("unknown scope")
   520  	}
   521  
   522  	opts := listOptions{}
   523  
   524  	for _, o := range options {
   525  		o(&opts)
   526  	}
   527  
   528  	fk.lock.Lock()
   529  	defer fk.lock.Unlock()
   530  
   531  	if opts.query == nil {
   532  		ris := make([]*apiv1.ResourceInstance, len(fk.resources))
   533  
   534  		i := 0
   535  		for _, ri := range fk.resources {
   536  			ris[i] = ri
   537  			i++
   538  		}
   539  		return ris, nil
   540  	}
   541  
   542  	fv := &FakeVisitor{
   543  		resources: fk,
   544  		set:       set{},
   545  	}
   546  
   547  	opts.query.Accept(fv)
   548  
   549  	ris := make([]*apiv1.ResourceInstance, len(fv.set))
   550  
   551  	i := 0
   552  	for k := range fv.set {
   553  		if ri, ok := fk.resources[k]; !ok {
   554  			panic(fmt.Sprintf("Resource %s in index but not in resource list", k))
   555  		} else {
   556  			ris[i] = ri
   557  			i++
   558  		}
   559  	}
   560  
   561  	return ris, nil
   562  }
   563  
   564  // Update -
   565  func (fk *fakeScoped) Update(ri *apiv1.ResourceInstance, opts ...UpdateOption) (*apiv1.ResourceInstance, error) {
   566  	return fk.UpdateCtx(context.Background(), ri, opts...)
   567  }
   568  
   569  // UpdateCtx -
   570  func (fk *fakeScoped) UpdateCtx(_ context.Context, ri *apiv1.ResourceInstance, opts ...UpdateOption) (*apiv1.ResourceInstance, error) {
   571  	if fk == nil {
   572  		return nil, notFound(ri.Metadata.Scope.Name, ri.Metadata.Scope.Kind)
   573  	}
   574  
   575  	fk.lock.Lock()
   576  	defer fk.lock.Unlock()
   577  
   578  	return fk.update(ri, opts...)
   579  }
   580  
   581  func (fk *fakeScoped) create(ri *apiv1.ResourceInstance, opts ...CreateOption) (*apiv1.ResourceInstance, error) {
   582  
   583  	co := createOptions{}
   584  
   585  	for _, opt := range opts {
   586  		opt(&co)
   587  	}
   588  
   589  	if ri.Name == "" {
   590  		return nil, fmt.Errorf("empty resource name: %v", ri)
   591  	}
   592  
   593  	if ex, ok := fk.resources[ri.Name]; ok {
   594  		return nil, fmt.Errorf("existing resource: %v", ex)
   595  	}
   596  
   597  	created := &apiv1.ResourceInstance{
   598  		ResourceMeta: apiv1.ResourceMeta{
   599  			Name:             ri.Name,
   600  			Title:            ri.Title,
   601  			GroupVersionKind: fk.GroupVersionKind,
   602  			Metadata: apiv1.Metadata{
   603  				ID: uuid.New().String(),
   604  				Audit: apiv1.AuditMetadata{
   605  					CreateTimestamp: apiv1.Time(time.Now()),
   606  					CreateUserID:    co.impersonateUserID,
   607  					ModifyTimestamp: apiv1.Time(time.Now()),
   608  					ModifyUserID:    co.impersonateUserID,
   609  				},
   610  				Scope:           fk.ms,
   611  				ResourceVersion: "0",
   612  				References:      nil,
   613  				State:           "",
   614  			},
   615  			Attributes: ri.Attributes,
   616  			Tags:       ri.Tags,
   617  		},
   618  		Spec: ri.Spec,
   619  	}
   620  
   621  	fk.attributeIndex.Update([]string{}, attrsAsIdxs(created.Attributes), created.Name)
   622  	fk.tagsIndex.Update([]string{}, created.Tags, created.Name)
   623  
   624  	fk.resources[ri.Name] = created
   625  
   626  	fk.handler.Handle(event(apiv1.ResourceEntryCreatedEvent, created))
   627  
   628  	return created, nil
   629  }
   630  
   631  func (fk *fakeScoped) update(ri *apiv1.ResourceInstance, opts ...UpdateOption) (*apiv1.ResourceInstance, error) {
   632  	uo := updateOptions{}
   633  
   634  	for _, opt := range opts {
   635  		opt(&uo)
   636  	}
   637  
   638  	if ri.Name == "" {
   639  		return nil, notFound(ri.Metadata.Scope.Name, ri.Metadata.Scope.Kind)
   640  	}
   641  
   642  	prev, ok := fk.resources[ri.Name]
   643  	if !ok && uo.mergeFunc == nil {
   644  		return nil, notFoundInScope(ri.Name, fk.Kind, fk.ms.Name)
   645  	}
   646  
   647  	if uo.mergeFunc != nil {
   648  		merged, err := uo.mergeFunc(prev, ri)
   649  		if err != nil {
   650  			return nil, err
   651  		}
   652  
   653  		ri, err = merged.AsInstance()
   654  		if err != nil {
   655  			return nil, err
   656  		}
   657  
   658  		if !ok {
   659  			return fk.create(ri, CUserID(uo.impersonateUserID))
   660  		}
   661  	}
   662  
   663  	updated := &apiv1.ResourceInstance{
   664  		ResourceMeta: apiv1.ResourceMeta{
   665  			Name:             prev.Name,
   666  			Title:            ri.Title,
   667  			GroupVersionKind: prev.GroupVersionKind,
   668  			Metadata: apiv1.Metadata{
   669  				ID: prev.Metadata.ID,
   670  				Audit: apiv1.AuditMetadata{
   671  					CreateTimestamp: prev.Metadata.Audit.CreateTimestamp,
   672  					CreateUserID:    prev.Metadata.Audit.CreateUserID,
   673  					ModifyTimestamp: apiv1.Time(time.Now()),
   674  					ModifyUserID:    uo.impersonateUserID,
   675  				},
   676  				Scope:      prev.Metadata.Scope,
   677  				References: nil,
   678  				State:      "", // needed?
   679  			},
   680  			Attributes: ri.Attributes,
   681  			Tags:       ri.Tags,
   682  		},
   683  		Spec: ri.Spec,
   684  	}
   685  
   686  	// update indexes
   687  	fk.attributeIndex.Update(attrsAsIdxs(prev.Attributes), attrsAsIdxs(updated.Attributes), updated.Name)
   688  	fk.tagsIndex.Update(prev.Tags, updated.Tags, updated.Name)
   689  
   690  	fk.resources[ri.Name] = updated
   691  
   692  	fk.handler.Handle(event(apiv1.ResourceEntryUpdatedEvent, updated))
   693  
   694  	return updated, nil
   695  }
   696  
   697  func (fk *fakeScoped) get(name string) (*apiv1.ResourceInstance, error) {
   698  	if fk == nil {
   699  		return nil, &NotFoundError{[]apiv1.Error{}}
   700  	}
   701  
   702  	ris, ok := fk.resources[name]
   703  	if !ok {
   704  		return nil, notFoundInScope(name, fk.Kind, fk.ms.Name)
   705  	}
   706  
   707  	return ris, nil
   708  }
   709  
   710  func (fk *fakeScoped) deleteAll() error {
   711  	fk.lock.Lock()
   712  	defer fk.lock.Unlock()
   713  
   714  	for _, ri := range fk.resources {
   715  		fk.handler.Handle(event(apiv1.ResourceEntryDeletedEvent, ri))
   716  	}
   717  
   718  	*fk = fakeScoped{}
   719  
   720  	return nil
   721  }
   722  
   723  type delegatingEventHandler struct {
   724  	wrapped EventHandler
   725  }
   726  
   727  // Handle -
   728  func (dh *delegatingEventHandler) Handle(e *apiv1.Event) {
   729  	if dh != nil && dh.wrapped != nil {
   730  		go func() {
   731  			if dh != nil && dh.wrapped != nil {
   732  				dh.wrapped.Handle(e)
   733  			}
   734  		}()
   735  	}
   736  }
   737  
   738  type fakeClientBase struct {
   739  	handler *delegatingEventHandler
   740  	groups  map[string]fakeGroup
   741  }
   742  
   743  type fakeClient struct {
   744  	fakeClientBase
   745  	Unscoped
   746  }
   747  
   748  func newFakeKind(gvk apiv1.GroupVersionKind, ms apiv1.MetadataScope, handler EventHandler) *fakeScoped {
   749  	return &fakeScoped{
   750  		gvk,
   751  		ms,
   752  		map[string]*apiv1.ResourceInstance{},
   753  		index{},
   754  		index{},
   755  		&sync.Mutex{},
   756  		handler,
   757  	}
   758  }
   759  
   760  // NewFakeClient -
   761  func NewFakeClient(is ...apiv1.Interface) (*fakeClientBase, error) {
   762  	handler := &delegatingEventHandler{}
   763  	groups := map[string]fakeGroup{}
   764  
   765  	for _, gvk := range apiv1.GVKSet() {
   766  
   767  		group, ok := groups[gvk.Group]
   768  		if !ok {
   769  			group = map[string]fakeVersion{}
   770  			groups[gvk.Group] = group
   771  		}
   772  
   773  		version, ok := group[gvk.APIVersion]
   774  		if !ok {
   775  			version = fakeVersion(map[string]*fakeUnscoped{})
   776  			group[gvk.APIVersion] = version
   777  		}
   778  
   779  		sk, ok := apiv1.GetScope(gvk.GroupKind)
   780  		if !ok {
   781  			panic(fmt.Sprintf("no scope for gvk: %s", gvk))
   782  		}
   783  
   784  		if sk != "" {
   785  			scope, ok := version[sk]
   786  			if !ok {
   787  				scope = &fakeUnscoped{
   788  					*newFakeKind(
   789  						apiv1.GroupVersionKind{
   790  							GroupKind: apiv1.GroupKind{
   791  								Group: gvk.Group,
   792  								Kind:  sk,
   793  							},
   794  							APIVersion: gvk.APIVersion,
   795  						},
   796  						apiv1.MetadataScope{},
   797  						handler,
   798  					),
   799  					map[string]fakeByScope{},
   800  				}
   801  				version[sk] = scope
   802  			}
   803  
   804  			_, ok = scope.scopedKinds[gvk.Kind]
   805  			if !ok {
   806  				scope.scopedKinds[gvk.Kind] = fakeByScope{
   807  					GroupVersionKind: gvk,
   808  					scopeKind:        scope.GroupKind,
   809  					fks:              map[string]*fakeScoped{},
   810  				}
   811  			}
   812  
   813  			continue
   814  		}
   815  
   816  		if _, ok := version[gvk.Kind]; !ok {
   817  			version[gvk.Kind] = &fakeUnscoped{
   818  				*newFakeKind(
   819  					gvk,
   820  
   821  					apiv1.MetadataScope{},
   822  					handler,
   823  				),
   824  				map[string]fakeByScope{},
   825  			}
   826  		}
   827  	}
   828  
   829  	client := &fakeClientBase{handler, groups}
   830  
   831  	// pass through and create unscoped resources
   832  	for _, i := range is {
   833  		ri, err := i.AsInstance()
   834  		if err != nil {
   835  			return nil, err
   836  		}
   837  
   838  		sk, ok := apiv1.GetScope(ri.GroupKind)
   839  		if !ok {
   840  			return nil, fmt.Errorf("no scope kind or unknown kind for ri: %v", ri)
   841  		}
   842  		if sk != "" {
   843  			continue
   844  		}
   845  
   846  		c, err := client.ForKind(ri.GroupVersionKind)
   847  		if err != nil {
   848  			return nil, err
   849  		}
   850  
   851  		_, err = c.Create(ri)
   852  		if err != nil {
   853  			return nil, err
   854  		}
   855  	}
   856  
   857  	// pass through and create scoped resources
   858  	for _, i := range is {
   859  		ri, err := i.AsInstance()
   860  		if err != nil {
   861  			return nil, err
   862  		}
   863  
   864  		sk, ok := apiv1.GetScope(ri.GroupKind)
   865  		if !ok {
   866  			return nil, fmt.Errorf("no scope kind or unknown kind for ri: %v", ri)
   867  		}
   868  		if sk == "" {
   869  			continue
   870  		}
   871  
   872  		noScope, err := client.ForKind(ri.GroupVersionKind)
   873  		if err != nil {
   874  			return nil, err
   875  		}
   876  
   877  		c := noScope.WithScope(ri.Metadata.Scope.Name)
   878  
   879  		_, err = c.Create(ri)
   880  		if err != nil {
   881  			return nil, err
   882  		}
   883  	}
   884  
   885  	return client, nil
   886  }
   887  
   888  // ForKind -
   889  func (fcb fakeClientBase) ForKind(gvk apiv1.GroupVersionKind) (Unscoped, error) {
   890  	sk, ok := apiv1.GetScope(gvk.GroupKind)
   891  	if !ok {
   892  		panic(fmt.Sprintf("no scope for gvk: %s", gvk))
   893  	}
   894  
   895  	if sk == "" {
   896  		return fcb.groups[gvk.Group][gvk.APIVersion][gvk.Kind], nil
   897  	}
   898  
   899  	return fcb.groups[gvk.Group][gvk.APIVersion][sk].scopedKinds[gvk.Kind], nil
   900  }
   901  
   902  // SetHandler -
   903  func (fcb fakeClientBase) SetHandler(ev EventHandler) {
   904  	fcb.handler.wrapped = ev
   905  }