github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/cmd/sinker/main_test.go (about)

     1  /*
     2  Copyright 2016 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 main
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/sirupsen/logrus"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/labels"
    28  
    29  	"k8s.io/test-infra/prow/config"
    30  	"k8s.io/test-infra/prow/kube"
    31  )
    32  
    33  type fakeClient struct {
    34  	Pods     []kube.Pod
    35  	ProwJobs []kube.ProwJob
    36  
    37  	DeletedPods     []kube.Pod
    38  	DeletedProwJobs []kube.ProwJob
    39  }
    40  
    41  func (c *fakeClient) ListPods(selector string) ([]kube.Pod, error) {
    42  	s, err := labels.Parse(selector)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	pl := make([]kube.Pod, 0, len(c.Pods))
    47  	for _, p := range c.Pods {
    48  		if s.Matches(labels.Set(p.ObjectMeta.Labels)) {
    49  			pl = append(pl, p)
    50  		}
    51  	}
    52  	return pl, nil
    53  }
    54  
    55  func (c *fakeClient) ListProwJobs(selector string) ([]kube.ProwJob, error) {
    56  	s, err := labels.Parse(selector)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  	jl := make([]kube.ProwJob, 0, len(c.ProwJobs))
    61  	for _, j := range c.ProwJobs {
    62  		if s.Matches(labels.Set(j.ObjectMeta.Labels)) {
    63  			jl = append(jl, j)
    64  		}
    65  	}
    66  	return jl, nil
    67  }
    68  
    69  func (c *fakeClient) DeleteProwJob(name string) error {
    70  	for i, j := range c.ProwJobs {
    71  		if j.ObjectMeta.Name == name {
    72  			c.ProwJobs = append(c.ProwJobs[:i], c.ProwJobs[i+1:]...)
    73  			c.DeletedProwJobs = append(c.DeletedProwJobs, j)
    74  			return nil
    75  		}
    76  	}
    77  	return fmt.Errorf("prowjob %s not found", name)
    78  }
    79  
    80  func (c *fakeClient) DeletePod(name string) error {
    81  	for i, p := range c.Pods {
    82  		if p.ObjectMeta.Name == name {
    83  			c.Pods = append(c.Pods[:i], c.Pods[i+1:]...)
    84  			c.DeletedPods = append(c.DeletedPods, p)
    85  			return nil
    86  		}
    87  	}
    88  	return fmt.Errorf("pod %s not found", name)
    89  }
    90  
    91  const (
    92  	maxProwJobAge = 2 * 24 * time.Hour
    93  	maxPodAge     = 12 * time.Hour
    94  )
    95  
    96  type fca struct {
    97  	c *config.Config
    98  }
    99  
   100  func newFakeConfigAgent() *fca {
   101  	return &fca{
   102  		c: &config.Config{
   103  			ProwConfig: config.ProwConfig{
   104  				Sinker: config.Sinker{
   105  					MaxProwJobAge: maxProwJobAge,
   106  					MaxPodAge:     maxPodAge,
   107  				},
   108  			},
   109  			JobConfig: config.JobConfig{
   110  				Periodics: []config.Periodic{
   111  					{JobBase: config.JobBase{Name: "retester"}},
   112  				},
   113  			},
   114  		},
   115  	}
   116  
   117  }
   118  
   119  func (f *fca) Config() *config.Config {
   120  	return f.c
   121  }
   122  
   123  func startTime(s time.Time) *metav1.Time {
   124  	start := metav1.NewTime(s)
   125  	return &start
   126  }
   127  
   128  func TestClean(t *testing.T) {
   129  
   130  	pods := []kube.Pod{
   131  		{
   132  			ObjectMeta: metav1.ObjectMeta{
   133  				Name: "old-failed",
   134  				Labels: map[string]string{
   135  					kube.CreatedByProw: "true",
   136  				},
   137  			},
   138  			Status: kube.PodStatus{
   139  				Phase:     kube.PodFailed,
   140  				StartTime: startTime(time.Now().Add(-maxPodAge).Add(-time.Second)),
   141  			},
   142  		},
   143  		{
   144  			ObjectMeta: metav1.ObjectMeta{
   145  				Name: "old-succeeded",
   146  				Labels: map[string]string{
   147  					kube.CreatedByProw: "true",
   148  				},
   149  			},
   150  			Status: kube.PodStatus{
   151  				Phase:     kube.PodSucceeded,
   152  				StartTime: startTime(time.Now().Add(-maxPodAge).Add(-time.Second)),
   153  			},
   154  		},
   155  		{
   156  			ObjectMeta: metav1.ObjectMeta{
   157  				Name: "old-just-complete",
   158  				Labels: map[string]string{
   159  					kube.CreatedByProw: "true",
   160  				},
   161  			},
   162  			Status: kube.PodStatus{
   163  				Phase:     kube.PodSucceeded,
   164  				StartTime: startTime(time.Now().Add(-maxPodAge).Add(-time.Second)),
   165  			},
   166  		},
   167  		{
   168  			ObjectMeta: metav1.ObjectMeta{
   169  				Name: "old-pending",
   170  				Labels: map[string]string{
   171  					kube.CreatedByProw: "true",
   172  				},
   173  			},
   174  			Status: kube.PodStatus{
   175  				Phase:     kube.PodPending,
   176  				StartTime: startTime(time.Now().Add(-maxPodAge).Add(-time.Second)),
   177  			},
   178  		},
   179  		{
   180  			ObjectMeta: metav1.ObjectMeta{
   181  				Name: "old-pending-abort",
   182  				Labels: map[string]string{
   183  					kube.CreatedByProw: "true",
   184  				},
   185  			},
   186  			Status: kube.PodStatus{
   187  				Phase:     kube.PodPending,
   188  				StartTime: startTime(time.Now().Add(-maxPodAge).Add(-time.Second)),
   189  			},
   190  		},
   191  		{
   192  			ObjectMeta: metav1.ObjectMeta{
   193  				Name: "new-failed",
   194  				Labels: map[string]string{
   195  					kube.CreatedByProw: "true",
   196  				},
   197  			},
   198  			Status: kube.PodStatus{
   199  				Phase:     kube.PodFailed,
   200  				StartTime: startTime(time.Now().Add(-10 * time.Second)),
   201  			},
   202  		},
   203  		{
   204  			ObjectMeta: metav1.ObjectMeta{
   205  				Name: "old-running",
   206  				Labels: map[string]string{
   207  					kube.CreatedByProw: "true",
   208  				},
   209  			},
   210  			Status: kube.PodStatus{
   211  				Phase:     kube.PodRunning,
   212  				StartTime: startTime(time.Now().Add(-maxPodAge).Add(-time.Second)),
   213  			},
   214  		},
   215  		{
   216  			ObjectMeta: metav1.ObjectMeta{
   217  				Name: "unrelated-failed",
   218  				Labels: map[string]string{
   219  					kube.CreatedByProw: "not really",
   220  				},
   221  			},
   222  			Status: kube.PodStatus{
   223  				Phase:     kube.PodFailed,
   224  				StartTime: startTime(time.Now().Add(-maxPodAge).Add(-time.Second)),
   225  			},
   226  		},
   227  		{
   228  			ObjectMeta: metav1.ObjectMeta{
   229  				Name: "unrelated-complete",
   230  			},
   231  			Status: kube.PodStatus{
   232  				Phase:     kube.PodSucceeded,
   233  				StartTime: startTime(time.Now().Add(-maxPodAge).Add(-time.Second)),
   234  			},
   235  		},
   236  	}
   237  	deletedPods := []string{
   238  		"old-failed",
   239  		"old-succeeded",
   240  		"old-pending-abort",
   241  	}
   242  	setComplete := func(d time.Duration) *metav1.Time {
   243  		completed := metav1.NewTime(time.Now().Add(d))
   244  		return &completed
   245  	}
   246  	prowJobs := []kube.ProwJob{
   247  		{
   248  			ObjectMeta: metav1.ObjectMeta{
   249  				Name: "old-failed",
   250  			},
   251  			Status: kube.ProwJobStatus{
   252  				StartTime:      metav1.NewTime(time.Now().Add(-maxProwJobAge).Add(-time.Second)),
   253  				CompletionTime: setComplete(-time.Second),
   254  			},
   255  		},
   256  		{
   257  			ObjectMeta: metav1.ObjectMeta{
   258  				Name: "old-succeeded",
   259  			},
   260  			Status: kube.ProwJobStatus{
   261  				StartTime:      metav1.NewTime(time.Now().Add(-maxProwJobAge).Add(-time.Second)),
   262  				CompletionTime: setComplete(-time.Second),
   263  			},
   264  		},
   265  		{
   266  			ObjectMeta: metav1.ObjectMeta{
   267  				Name: "old-just-complete",
   268  			},
   269  			Status: kube.ProwJobStatus{
   270  				StartTime: metav1.NewTime(time.Now().Add(-maxProwJobAge).Add(-time.Second)),
   271  			},
   272  		},
   273  		{
   274  			ObjectMeta: metav1.ObjectMeta{
   275  				Name: "old-complete",
   276  			},
   277  			Status: kube.ProwJobStatus{
   278  				StartTime:      metav1.NewTime(time.Now().Add(-maxProwJobAge).Add(-time.Second)),
   279  				CompletionTime: setComplete(-time.Second),
   280  			},
   281  		},
   282  		{
   283  			ObjectMeta: metav1.ObjectMeta{
   284  				Name: "old-incomplete",
   285  			},
   286  			Status: kube.ProwJobStatus{
   287  				StartTime: metav1.NewTime(time.Now().Add(-maxProwJobAge).Add(-time.Second)),
   288  			},
   289  		},
   290  		{
   291  			ObjectMeta: metav1.ObjectMeta{
   292  				Name: "old-pending",
   293  			},
   294  			Status: kube.ProwJobStatus{
   295  				StartTime: metav1.NewTime(time.Now().Add(-maxProwJobAge).Add(-time.Second)),
   296  			},
   297  		},
   298  		{
   299  			ObjectMeta: metav1.ObjectMeta{
   300  				Name: "old-pending-abort",
   301  			},
   302  			Status: kube.ProwJobStatus{
   303  				StartTime:      metav1.NewTime(time.Now().Add(-maxProwJobAge).Add(-time.Second)),
   304  				CompletionTime: setComplete(-time.Second),
   305  			},
   306  		},
   307  		{
   308  			ObjectMeta: metav1.ObjectMeta{
   309  				Name: "new",
   310  			},
   311  			Status: kube.ProwJobStatus{
   312  				StartTime: metav1.NewTime(time.Now().Add(-time.Second)),
   313  			},
   314  		},
   315  		{
   316  			ObjectMeta: metav1.ObjectMeta{
   317  				Name: "newer-periodic",
   318  			},
   319  			Spec: kube.ProwJobSpec{
   320  				Type: kube.PeriodicJob,
   321  				Job:  "retester",
   322  			},
   323  			Status: kube.ProwJobStatus{
   324  				StartTime:      metav1.NewTime(time.Now().Add(-maxProwJobAge).Add(-time.Second)),
   325  				CompletionTime: setComplete(-time.Second),
   326  			},
   327  		},
   328  		{
   329  			ObjectMeta: metav1.ObjectMeta{
   330  				Name: "older-periodic",
   331  			},
   332  			Spec: kube.ProwJobSpec{
   333  				Type: kube.PeriodicJob,
   334  				Job:  "retester",
   335  			},
   336  			Status: kube.ProwJobStatus{
   337  				StartTime:      metav1.NewTime(time.Now().Add(-maxProwJobAge).Add(-time.Minute)),
   338  				CompletionTime: setComplete(-time.Minute),
   339  			},
   340  		},
   341  		{
   342  			ObjectMeta: metav1.ObjectMeta{
   343  				Name: "oldest-periodic",
   344  			},
   345  			Spec: kube.ProwJobSpec{
   346  				Type: kube.PeriodicJob,
   347  				Job:  "retester",
   348  			},
   349  			Status: kube.ProwJobStatus{
   350  				StartTime:      metav1.NewTime(time.Now().Add(-maxProwJobAge).Add(-time.Hour)),
   351  				CompletionTime: setComplete(-time.Hour),
   352  			},
   353  		},
   354  		{
   355  			ObjectMeta: metav1.ObjectMeta{
   356  				Name: "old-failed-trusted",
   357  			},
   358  			Status: kube.ProwJobStatus{
   359  				StartTime:      metav1.NewTime(time.Now().Add(-maxProwJobAge).Add(-time.Second)),
   360  				CompletionTime: setComplete(-time.Second),
   361  			},
   362  		},
   363  	}
   364  	deletedProwJobs := []string{
   365  		"old-failed",
   366  		"old-succeeded",
   367  		"old-complete",
   368  		"old-pending-abort",
   369  		"older-periodic",
   370  		"oldest-periodic",
   371  		"old-failed-trusted",
   372  	}
   373  	podsTrusted := []kube.Pod{
   374  		{
   375  			ObjectMeta: metav1.ObjectMeta{
   376  				Name: "old-failed-trusted",
   377  				Labels: map[string]string{
   378  					kube.CreatedByProw: "true",
   379  				},
   380  			},
   381  			Status: kube.PodStatus{
   382  				Phase:     kube.PodFailed,
   383  				StartTime: startTime(time.Now().Add(-maxPodAge).Add(-time.Second)),
   384  			},
   385  		},
   386  	}
   387  	deletedPodsTrusted := []string{"old-failed-trusted"}
   388  
   389  	kc := &fakeClient{
   390  		Pods:     pods,
   391  		ProwJobs: prowJobs,
   392  	}
   393  	kcTrusted := &fakeClient{
   394  		Pods:     podsTrusted,
   395  		ProwJobs: nil,
   396  	}
   397  	// Run
   398  	c := controller{
   399  		logger:      logrus.WithField("component", "sinker"),
   400  		kc:          kc,
   401  		pkcs:        map[string]kubeClient{kube.DefaultClusterAlias: kc, "trusted": kcTrusted},
   402  		configAgent: newFakeConfigAgent(),
   403  	}
   404  	c.clean()
   405  	// Check
   406  	check := func(kc *fakeClient, deletedPods, deletedProwJobs []string) {
   407  		if len(deletedPods) != len(kc.DeletedPods) {
   408  			var got []string
   409  			for _, pj := range kc.DeletedPods {
   410  				got = append(got, pj.ObjectMeta.Name)
   411  			}
   412  			t.Errorf("Deleted wrong number of pods: got %d (%v), expected %d (%v)",
   413  				len(got), strings.Join(got, ", "), len(deletedPods), strings.Join(deletedPods, ", "))
   414  		}
   415  		for _, n := range deletedPods {
   416  			found := false
   417  			for _, p := range kc.DeletedPods {
   418  				if p.ObjectMeta.Name == n {
   419  					found = true
   420  				}
   421  			}
   422  			if !found {
   423  				t.Errorf("Did not delete pod %s", n)
   424  			}
   425  		}
   426  		if len(deletedProwJobs) != len(kc.DeletedProwJobs) {
   427  			var got []string
   428  			for _, pj := range kc.DeletedProwJobs {
   429  				got = append(got, pj.ObjectMeta.Name)
   430  			}
   431  			t.Errorf("Deleted wrong number of prowjobs: got %d (%s), expected %d (%s)",
   432  				len(got), strings.Join(got, ", "), len(deletedProwJobs), strings.Join(deletedProwJobs, ", "))
   433  		}
   434  		for _, n := range deletedProwJobs {
   435  			found := false
   436  			for _, j := range kc.DeletedProwJobs {
   437  				if j.ObjectMeta.Name == n {
   438  					found = true
   439  				}
   440  			}
   441  			if !found {
   442  				t.Errorf("Did not delete prowjob %s", n)
   443  			}
   444  		}
   445  	}
   446  	check(kc, deletedPods, deletedProwJobs)
   447  	check(kcTrusted, deletedPodsTrusted, nil)
   448  }