github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/boskos/ranch/ranch_test.go (about)

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package ranch
    18  
    19  import (
    20  	"reflect"
    21  	"sort"
    22  	"testing"
    23  	"time"
    24  
    25  	"k8s.io/test-infra/boskos/common"
    26  	"k8s.io/test-infra/boskos/crds"
    27  )
    28  
    29  func MakeTestRanch(resources []common.Resource) *Ranch {
    30  	rs := crds.NewCRDStorage(crds.NewTestResourceClient())
    31  	s, _ := NewStorage(rs, "")
    32  	for _, res := range resources {
    33  		s.AddResource(res)
    34  	}
    35  	r, _ := NewRanch("", s)
    36  	return r
    37  }
    38  
    39  func AreErrorsEqual(got error, expect error) bool {
    40  	if got == nil && expect == nil {
    41  		return true
    42  	}
    43  
    44  	if got == nil || expect == nil {
    45  		return false
    46  	}
    47  
    48  	switch got.(type) {
    49  	case *OwnerNotMatch:
    50  		if o, ok := expect.(*OwnerNotMatch); ok {
    51  			if o.request == got.(*OwnerNotMatch).request && o.owner == got.(*OwnerNotMatch).owner {
    52  				return true
    53  			}
    54  		}
    55  		return false
    56  	case *ResourceNotFound:
    57  		if o, ok := expect.(*ResourceNotFound); ok {
    58  			if o.name == got.(*ResourceNotFound).name {
    59  				return true
    60  			}
    61  		}
    62  		return false
    63  	case *StateNotMatch:
    64  		if o, ok := expect.(*StateNotMatch); ok {
    65  			if o.expect == got.(*StateNotMatch).expect && o.current == got.(*StateNotMatch).current {
    66  				return true
    67  			}
    68  		}
    69  		return false
    70  	default:
    71  		return false
    72  	}
    73  }
    74  
    75  func TestAcquire(t *testing.T) {
    76  	FakeNow := time.Now()
    77  	var testcases = []struct {
    78  		name      string
    79  		resources []common.Resource
    80  		owner     string
    81  		rtype     string
    82  		state     string
    83  		dest      string
    84  		expectErr error
    85  	}{
    86  		{
    87  			name:      "ranch has no resource",
    88  			resources: []common.Resource{},
    89  			owner:     "user",
    90  			rtype:     "t",
    91  			state:     "s",
    92  			dest:      "d",
    93  			expectErr: &ResourceNotFound{"t"},
    94  		},
    95  		{
    96  			name: "no match type",
    97  			resources: []common.Resource{
    98  				common.NewResource("res", "wrong", "s", "", FakeNow),
    99  			},
   100  			owner:     "user",
   101  			rtype:     "t",
   102  			state:     "s",
   103  			dest:      "d",
   104  			expectErr: &ResourceNotFound{"t"},
   105  		},
   106  		{
   107  			name: "no match state",
   108  			resources: []common.Resource{
   109  				common.NewResource("res", "t", "wrong", "", FakeNow),
   110  			},
   111  			owner:     "user",
   112  			rtype:     "t",
   113  			state:     "s",
   114  			dest:      "d",
   115  			expectErr: &ResourceNotFound{"t"},
   116  		},
   117  		{
   118  			name: common.Busy,
   119  			resources: []common.Resource{
   120  				common.NewResource("res", "t", "s", "foo", FakeNow),
   121  			},
   122  			owner:     "user",
   123  			rtype:     "t",
   124  			state:     "s",
   125  			dest:      "d",
   126  			expectErr: &ResourceNotFound{"t"},
   127  		},
   128  		{
   129  			name: "ok",
   130  			resources: []common.Resource{
   131  				common.NewResource("res", "t", "s", "", FakeNow),
   132  			},
   133  			owner:     "user",
   134  			rtype:     "t",
   135  			state:     "s",
   136  			dest:      "d",
   137  			expectErr: nil,
   138  		},
   139  	}
   140  
   141  	for _, tc := range testcases {
   142  		c := MakeTestRanch(tc.resources)
   143  		res, err := c.Acquire(tc.rtype, tc.state, tc.dest, tc.owner)
   144  		if !AreErrorsEqual(err, tc.expectErr) {
   145  			t.Errorf("%s - Got error %v, expect error %v", tc.name, err, tc.expectErr)
   146  			continue
   147  		}
   148  
   149  		resources, err2 := c.Storage.GetResources()
   150  		if err2 != nil {
   151  			t.Errorf("failed to get resources")
   152  			continue
   153  		}
   154  
   155  		if err == nil {
   156  			if res.State != tc.dest {
   157  				t.Errorf("%s - Wrong final state. Got %v, expect %v", tc.name, res.State, tc.dest)
   158  			}
   159  			if !reflect.DeepEqual(*res, resources[0]) {
   160  				t.Errorf("%s - Wrong resource. Got %v, expect %v", tc.name, res, resources[0])
   161  			} else if !res.LastUpdate.After(FakeNow) {
   162  				t.Errorf("%s - LastUpdate did not update.", tc.name)
   163  			}
   164  		} else {
   165  			for _, res := range resources {
   166  				if res.LastUpdate != FakeNow {
   167  					t.Errorf("%s - LastUpdate should not update. Got %v, expect %v", tc.name, resources[0].LastUpdate, FakeNow)
   168  				}
   169  			}
   170  		}
   171  	}
   172  }
   173  
   174  func TestAcquireRoundRobin(t *testing.T) {
   175  	FakeNow := time.Now()
   176  	var resources []common.Resource
   177  	for i := 1; i < 5; i++ {
   178  		resources = append(resources, common.NewResource("res-1", "t", "s", "", FakeNow))
   179  	}
   180  
   181  	results := map[string]int{}
   182  
   183  	c := MakeTestRanch(resources)
   184  	for i := 0; i < 4; i++ {
   185  		res, err := c.Acquire("t", "s", "d", "foo")
   186  		if err != nil {
   187  			t.Fatalf("Unexpected error: %v", err)
   188  		}
   189  		_, found := results[res.Name]
   190  		if found {
   191  			t.Errorf("resource %s was used more than once", res.Name)
   192  		}
   193  		c.Release(res.Name, "s", "foo")
   194  	}
   195  }
   196  
   197  func TestRelease(t *testing.T) {
   198  	FakeNow := time.Now()
   199  	var testcases = []struct {
   200  		name      string
   201  		resources []common.Resource
   202  		resName   string
   203  		owner     string
   204  		dest      string
   205  		expectErr error
   206  	}{
   207  		{
   208  			name:      "ranch has no resource",
   209  			resources: []common.Resource{},
   210  			resName:   "res",
   211  			owner:     "user",
   212  			dest:      "d",
   213  			expectErr: &ResourceNotFound{"res"},
   214  		},
   215  		{
   216  			name: "wrong owner",
   217  			resources: []common.Resource{
   218  				common.NewResource("res", "t", "s", "merlin", FakeNow),
   219  			},
   220  			resName:   "res",
   221  			owner:     "user",
   222  			dest:      "d",
   223  			expectErr: &OwnerNotMatch{"merlin", "user"},
   224  		},
   225  		{
   226  			name: "no match name",
   227  			resources: []common.Resource{
   228  				common.NewResource("foo", "t", "s", "merlin", FakeNow),
   229  			},
   230  			resName:   "res",
   231  			owner:     "user",
   232  			dest:      "d",
   233  			expectErr: &ResourceNotFound{"res"},
   234  		},
   235  		{
   236  			name: "ok",
   237  			resources: []common.Resource{
   238  				common.NewResource("res", "t", "s", "merlin", FakeNow),
   239  			},
   240  			resName:   "res",
   241  			owner:     "merlin",
   242  			dest:      "d",
   243  			expectErr: nil,
   244  		},
   245  	}
   246  
   247  	for _, tc := range testcases {
   248  		c := MakeTestRanch(tc.resources)
   249  		err := c.Release(tc.resName, tc.dest, tc.owner)
   250  		if !AreErrorsEqual(err, tc.expectErr) {
   251  			t.Errorf("%s - Got error %v, expect error %v", tc.name, err, tc.expectErr)
   252  			continue
   253  		}
   254  		resources, err2 := c.Storage.GetResources()
   255  		if err2 != nil {
   256  			t.Errorf("failed to get resources")
   257  			continue
   258  		}
   259  		if err == nil {
   260  			if resources[0].Owner != "" {
   261  				t.Errorf("%s - Wrong owner after release. Got %v, expect empty", tc.name, resources[0].Owner)
   262  			} else if resources[0].State != tc.dest {
   263  				t.Errorf("%s - Wrong state after release. Got %v, expect %v", tc.name, resources[0].State, tc.dest)
   264  			} else if !resources[0].LastUpdate.After(FakeNow) {
   265  				t.Errorf("%s - LastUpdate did not update.", tc.name)
   266  			}
   267  		} else {
   268  			for _, res := range resources {
   269  				if res.LastUpdate != FakeNow {
   270  					t.Errorf("%s - LastUpdate should not update. Got %v, expect %v", tc.name, resources[0].LastUpdate, FakeNow)
   271  				}
   272  			}
   273  		}
   274  	}
   275  }
   276  
   277  func TestReset(t *testing.T) {
   278  	FakeNow := time.Now()
   279  
   280  	var testcases = []struct {
   281  		name       string
   282  		resources  []common.Resource
   283  		rtype      string
   284  		state      string
   285  		dest       string
   286  		expire     time.Duration
   287  		hasContent bool
   288  	}{
   289  
   290  		{
   291  			name: "empty - has no owner",
   292  			resources: []common.Resource{
   293  				common.NewResource("res", "t", "s", "", FakeNow.Add(-time.Minute*20)),
   294  			},
   295  			rtype:  "t",
   296  			state:  "s",
   297  			expire: time.Minute * 10,
   298  			dest:   "d",
   299  		},
   300  		{
   301  			name: "empty - not expire",
   302  			resources: []common.Resource{
   303  				common.NewResource("res", "t", "s", "", FakeNow),
   304  			},
   305  			rtype:  "t",
   306  			state:  "s",
   307  			expire: time.Minute * 10,
   308  			dest:   "d",
   309  		},
   310  		{
   311  			name: "empty - no match type",
   312  			resources: []common.Resource{
   313  				common.NewResource("res", "wrong", "s", "", FakeNow.Add(-time.Minute*20)),
   314  			},
   315  			rtype:  "t",
   316  			state:  "s",
   317  			expire: time.Minute * 10,
   318  			dest:   "d",
   319  		},
   320  		{
   321  			name: "empty - no match state",
   322  			resources: []common.Resource{
   323  				common.NewResource("res", "t", "wrong", "", FakeNow.Add(-time.Minute*20)),
   324  			},
   325  			rtype:  "t",
   326  			state:  "s",
   327  			expire: time.Minute * 10,
   328  			dest:   "d",
   329  		},
   330  		{
   331  			name: "ok",
   332  			resources: []common.Resource{
   333  				common.NewResource("res", "t", "s", "user", FakeNow.Add(-time.Minute*20)),
   334  			},
   335  			rtype:      "t",
   336  			state:      "s",
   337  			expire:     time.Minute * 10,
   338  			dest:       "d",
   339  			hasContent: true,
   340  		},
   341  	}
   342  
   343  	for _, tc := range testcases {
   344  		c := MakeTestRanch(tc.resources)
   345  		rmap, err := c.Reset(tc.rtype, tc.state, tc.expire, tc.dest)
   346  		if err != nil {
   347  			t.Errorf("failed to reset %v", err)
   348  		}
   349  
   350  		if !tc.hasContent {
   351  			if len(rmap) != 0 {
   352  				t.Errorf("%s - Expect empty map. Got %v", tc.name, rmap)
   353  			}
   354  		} else {
   355  			if owner, ok := rmap["res"]; !ok || owner != "user" {
   356  				t.Errorf("%s - Expect res - user. Got %v", tc.name, rmap)
   357  			}
   358  			resources, err := c.Storage.GetResources()
   359  			if err != nil {
   360  				t.Errorf("failed to get resources")
   361  				continue
   362  			}
   363  			if !resources[0].LastUpdate.After(FakeNow) {
   364  				t.Errorf("%s - LastUpdate did not update.", tc.name)
   365  			}
   366  		}
   367  	}
   368  }
   369  
   370  func TestUpdate(t *testing.T) {
   371  	FakeNow := time.Now()
   372  
   373  	var testcases = []struct {
   374  		name      string
   375  		resources []common.Resource
   376  		resName   string
   377  		owner     string
   378  		state     string
   379  		expectErr error
   380  	}{
   381  		{
   382  			name:      "ranch has no resource",
   383  			resources: []common.Resource{},
   384  			resName:   "res",
   385  			owner:     "user",
   386  			state:     "s",
   387  			expectErr: &ResourceNotFound{"res"},
   388  		},
   389  		{
   390  			name: "wrong owner",
   391  			resources: []common.Resource{
   392  				common.NewResource("res", "t", "s", "merlin", FakeNow),
   393  			},
   394  			resName:   "res",
   395  			owner:     "user",
   396  			state:     "s",
   397  			expectErr: &OwnerNotMatch{"user", "merlin"},
   398  		},
   399  		{
   400  			name: "wrong state",
   401  			resources: []common.Resource{
   402  				common.NewResource("res", "t", "s", "merlin", FakeNow),
   403  			},
   404  			resName:   "res",
   405  			owner:     "merlin",
   406  			state:     "foo",
   407  			expectErr: &StateNotMatch{"s", "foo"},
   408  		},
   409  		{
   410  			name: "no matched resource",
   411  			resources: []common.Resource{
   412  				common.NewResource("foo", "t", "s", "merlin", FakeNow),
   413  			},
   414  			resName:   "res",
   415  			owner:     "merlin",
   416  			state:     "s",
   417  			expectErr: &ResourceNotFound{"res"},
   418  		},
   419  		{
   420  			name: "ok",
   421  			resources: []common.Resource{
   422  				common.NewResource("res", "t", "s", "merlin", FakeNow),
   423  			},
   424  			resName: "res",
   425  			owner:   "merlin",
   426  			state:   "s",
   427  		},
   428  	}
   429  
   430  	for _, tc := range testcases {
   431  		c := MakeTestRanch(tc.resources)
   432  		err := c.Update(tc.resName, tc.owner, tc.state, nil)
   433  		if !AreErrorsEqual(err, tc.expectErr) {
   434  			t.Errorf("%s - Got error %v, expect error %v", tc.name, err, tc.expectErr)
   435  			continue
   436  		}
   437  
   438  		resources, err2 := c.Storage.GetResources()
   439  		if err2 != nil {
   440  			t.Errorf("failed to get resources")
   441  			continue
   442  		}
   443  
   444  		if err == nil {
   445  			if resources[0].Owner != tc.owner {
   446  				t.Errorf("%s - Wrong owner after release. Got %v, expect %v", tc.name, resources[0].Owner, tc.owner)
   447  			} else if resources[0].State != tc.state {
   448  				t.Errorf("%s - Wrong state after release. Got %v, expect %v", tc.name, resources[0].State, tc.state)
   449  			} else if !resources[0].LastUpdate.After(FakeNow) {
   450  				t.Errorf("%s - LastUpdate did not update.", tc.name)
   451  			}
   452  		} else {
   453  			for _, res := range resources {
   454  				if res.LastUpdate != FakeNow {
   455  					t.Errorf("%s - LastUpdate should not update. Got %v, expect %v", tc.name, resources[0].LastUpdate, FakeNow)
   456  				}
   457  			}
   458  		}
   459  	}
   460  }
   461  
   462  func TestMetric(t *testing.T) {
   463  	var testcases = []struct {
   464  		name         string
   465  		resources    []common.Resource
   466  		metricType   string
   467  		expectErr    error
   468  		expectMetric common.Metric
   469  	}{
   470  		{
   471  			name:       "ranch has no resource",
   472  			resources:  []common.Resource{},
   473  			metricType: "t",
   474  			expectErr:  &ResourceNotFound{"t"},
   475  		},
   476  		{
   477  			name: "no matching resource",
   478  			resources: []common.Resource{
   479  				common.NewResource("res", "t", "s", "merlin", time.Now()),
   480  			},
   481  			metricType: "foo",
   482  			expectErr:  &ResourceNotFound{"foo"},
   483  		},
   484  		{
   485  			name: "one resource",
   486  			resources: []common.Resource{
   487  				common.NewResource("res", "t", "s", "merlin", time.Now()),
   488  			},
   489  			metricType: "t",
   490  			expectMetric: common.Metric{
   491  				Type: "t",
   492  				Current: map[string]int{
   493  					"s": 1,
   494  				},
   495  				Owners: map[string]int{
   496  					"merlin": 1,
   497  				},
   498  			},
   499  		},
   500  		{
   501  			name: "multiple resources",
   502  			resources: []common.Resource{
   503  				common.NewResource("res-1", "t", "s", "merlin", time.Now()),
   504  				common.NewResource("res-2", "t", "p", "pony", time.Now()),
   505  				common.NewResource("res-3", "t", "s", "pony", time.Now()),
   506  				common.NewResource("res-4", "foo", "s", "pony", time.Now()),
   507  				common.NewResource("res-5", "t", "d", "merlin", time.Now()),
   508  			},
   509  			metricType: "t",
   510  			expectMetric: common.Metric{
   511  				Type: "t",
   512  				Current: map[string]int{
   513  					"s": 2,
   514  					"d": 1,
   515  					"p": 1,
   516  				},
   517  				Owners: map[string]int{
   518  					"merlin": 2,
   519  					"pony":   2,
   520  				},
   521  			},
   522  		},
   523  	}
   524  
   525  	for _, tc := range testcases {
   526  		c := MakeTestRanch(tc.resources)
   527  		metric, err := c.Metric(tc.metricType)
   528  		if !AreErrorsEqual(err, tc.expectErr) {
   529  			t.Errorf("%s - Got error %v, expect error %v", tc.name, err, tc.expectErr)
   530  			continue
   531  		}
   532  
   533  		if err == nil {
   534  			if !reflect.DeepEqual(metric, tc.expectMetric) {
   535  				t.Errorf("%s - wrong metric, got %v, want %v", tc.name, metric, tc.expectMetric)
   536  			}
   537  		}
   538  	}
   539  }
   540  
   541  func TestSyncResources(t *testing.T) {
   542  	var testcases = []struct {
   543  		name   string
   544  		oldRes []common.Resource
   545  		newRes []common.Resource
   546  		expect []common.Resource
   547  	}{
   548  		{
   549  			name: "empty",
   550  		},
   551  		{
   552  			name: "append",
   553  			newRes: []common.Resource{
   554  				common.NewResource("res", "t", "", "", time.Time{}),
   555  			},
   556  			expect: []common.Resource{
   557  				common.NewResource("res", "t", common.Free, "", time.Time{}),
   558  			},
   559  		},
   560  		{
   561  			name: "should not have a type change",
   562  			oldRes: []common.Resource{
   563  				common.NewResource("res", "t", "", "", time.Time{}),
   564  			},
   565  			newRes: []common.Resource{
   566  				common.NewResource("res", "d", "", "", time.Time{}),
   567  			},
   568  			expect: []common.Resource{
   569  				common.NewResource("res", "t", "", "", time.Time{}),
   570  			},
   571  		},
   572  		{
   573  			name: "delete",
   574  			oldRes: []common.Resource{
   575  				common.NewResource("res", "t", "", "", time.Time{}),
   576  			},
   577  		},
   578  		{
   579  			name: "delete busy",
   580  			oldRes: []common.Resource{
   581  				common.NewResource("res", "t", common.Busy, "o", time.Time{}),
   582  			},
   583  			expect: []common.Resource{
   584  				common.NewResource("res", "t", common.Busy, "o", time.Time{}),
   585  			},
   586  		},
   587  		{
   588  			name: "append and delete",
   589  			oldRes: []common.Resource{
   590  				common.NewResource("res-1", "t", "", "", time.Time{}),
   591  			},
   592  			newRes: []common.Resource{
   593  				common.NewResource("res-2", "t", "", "", time.Time{}),
   594  			},
   595  			expect: []common.Resource{
   596  				common.NewResource("res-2", "t", common.Free, "", time.Time{}),
   597  			},
   598  		},
   599  		{
   600  			name: "append and delete busy",
   601  			oldRes: []common.Resource{
   602  				common.NewResource("res-1", "t", common.Busy, "o", time.Time{}),
   603  			},
   604  			newRes: []common.Resource{
   605  				common.NewResource("res-2", "t", "", "", time.Time{}),
   606  			},
   607  			expect: []common.Resource{
   608  				common.NewResource("res-1", "t", common.Busy, "o", time.Time{}),
   609  				common.NewResource("res-2", "t", common.Free, "", time.Time{}),
   610  			},
   611  		},
   612  		{
   613  			name: "append/delete mixed type",
   614  			oldRes: []common.Resource{
   615  				common.NewResource("res-1", "t", "", "", time.Time{}),
   616  			},
   617  			newRes: []common.Resource{
   618  				common.NewResource("res-2", "t", "", "", time.Time{}),
   619  				common.NewResource("res-3", "t2", "", "", time.Time{}),
   620  			},
   621  			expect: []common.Resource{
   622  				common.NewResource("res-2", "t", "free", "", time.Time{}),
   623  				common.NewResource("res-3", "t2", "free", "", time.Time{}),
   624  			},
   625  		},
   626  	}
   627  
   628  	for _, tc := range testcases {
   629  		c := MakeTestRanch(tc.oldRes)
   630  		c.Storage.SyncResources(tc.newRes)
   631  		resources, err := c.Storage.GetResources()
   632  		if err != nil {
   633  			t.Errorf("failed to get resources")
   634  			continue
   635  		}
   636  		sort.Stable(common.ResourceByName(resources))
   637  		sort.Stable(common.ResourceByName(tc.expect))
   638  		if !reflect.DeepEqual(resources, tc.expect) {
   639  			t.Errorf("Test %v: got %v, expect %v", tc.name, resources, tc.expect)
   640  		}
   641  	}
   642  }