| // Copyright 2018 Google LLC | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| package gce | |
| import ( | |
| "context" | |
| "testing" | |
| "time" | |
| "cos.googlesource.com/cos/tools.git/src/pkg/config" | |
| "cos.googlesource.com/cos/tools.git/src/pkg/fakes" | |
| compute "google.golang.org/api/compute/v1" | |
| ) | |
| func fakeTime(current time.Time) *timePkg { | |
| fake := fakes.NewTime(current) | |
| return &timePkg{fake.Now, fake.Sleep} | |
| } | |
| func TestDeprecateInFamilyNoFamily(t *testing.T) { | |
| ctx := context.Background() | |
| newImage := &config.Image{&compute.Image{Name: "test-name"}, "test-project"} | |
| if err := DeprecateInFamily(ctx, nil, newImage, 0); err == nil { | |
| t.Error("DeprecateInFamily: did not fail when input image had no family") | |
| } | |
| } | |
| func TestDeprecateInFamilyNoItems(t *testing.T) { | |
| fakeGCE, client := fakes.GCEForTest(t, "test-project") | |
| defer fakeGCE.Close() | |
| ctx := context.Background() | |
| newImage := &config.Image{&compute.Image{Name: "test-name", Family: "test-family"}, "test-project"} | |
| if err := DeprecateInFamily(ctx, client, newImage, 0); err != nil { | |
| t.Logf("DeprecateInFamily(_, _, %v, 0)", newImage) | |
| t.Fatal(err) | |
| } | |
| if len(fakeGCE.Deprecated) != 0 { | |
| t.Errorf("an image was deprecated. Map: %v", fakeGCE.Deprecated) | |
| } | |
| } | |
| func TestDeprecateInFamilyIgnoreDeprecated(t *testing.T) { | |
| fakeGCE, client := fakes.GCEForTest(t, "test-project") | |
| defer fakeGCE.Close() | |
| ctx := context.Background() | |
| fakeGCE.Images.Items = []*compute.Image{{Deprecated: &compute.DeprecationStatus{}}} | |
| newImage := &config.Image{&compute.Image{Name: "test-name", Family: "test-family"}, "test-project"} | |
| if err := DeprecateInFamily(ctx, client, newImage, 0); err != nil { | |
| t.Logf("fakeGCE.Images.Items: %v", fakeGCE.Images.Items) | |
| t.Logf("DeprecateInFamily(_, _, %v, 0)", newImage) | |
| t.Fatal(err) | |
| } | |
| if len(fakeGCE.Deprecated) != 0 { | |
| t.Errorf("an image was deprecated. Map: %v", fakeGCE.Deprecated) | |
| } | |
| } | |
| func TestDeprecateInFamily(t *testing.T) { | |
| testDeprecateInFamilyData := []struct { | |
| testName string | |
| images []*compute.Image | |
| ops []*compute.Operation | |
| }{ | |
| { | |
| "DoneInstantly", | |
| []*compute.Image{ | |
| {Name: "dep-1"}, | |
| }, | |
| []*compute.Operation{ | |
| {Name: "op-1", Status: "DONE"}, | |
| }, | |
| }, | |
| { | |
| "RunningOpBeforeDone", | |
| []*compute.Image{ | |
| {Name: "dep-1"}, | |
| }, | |
| []*compute.Operation{ | |
| {Name: "op-1", Status: "RUNNING"}, | |
| {Name: "op-2", Status: "DONE"}, | |
| }, | |
| }, | |
| { | |
| "PendingOpBeforeDone", | |
| []*compute.Image{ | |
| {Name: "dep-1"}, | |
| }, | |
| []*compute.Operation{ | |
| {Name: "op-1", Status: "PENDING"}, | |
| {Name: "op-2", Status: "DONE"}, | |
| }, | |
| }, | |
| { | |
| "TwoRunningBeforeDone", | |
| []*compute.Image{ | |
| {Name: "dep-1"}, | |
| }, | |
| []*compute.Operation{ | |
| {Name: "op-1", Status: "RUNNING"}, | |
| {Name: "op-2", Status: "RUNNING"}, | |
| {Name: "op-3", Status: "DONE"}, | |
| }, | |
| }, | |
| { | |
| "TwoImages", | |
| []*compute.Image{ | |
| {Name: "dep-1"}, | |
| {Name: "dep-2"}, | |
| }, | |
| []*compute.Operation{ | |
| {Name: "op-1", Status: "DONE"}, | |
| {Name: "op-2", Status: "DONE"}, | |
| }, | |
| }, | |
| { | |
| "TwoImagesRunningBeforeDone", | |
| []*compute.Image{ | |
| {Name: "dep-1"}, | |
| {Name: "dep-2"}, | |
| }, | |
| []*compute.Operation{ | |
| {Name: "op-1", Status: "RUNNING"}, | |
| {Name: "op-1", Status: "DONE"}, | |
| {Name: "op-2", Status: "RUNNING"}, | |
| {Name: "op-2", Status: "DONE"}, | |
| }, | |
| }, | |
| } | |
| fakeGCE, client := fakes.GCEForTest(t, "test-project") | |
| defer fakeGCE.Close() | |
| for _, input := range testDeprecateInFamilyData { | |
| t.Run(input.testName, func(t *testing.T) { | |
| date := time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC) | |
| ctx := context.Background() | |
| fakeGCE.Images.Items = input.images | |
| fakeGCE.Operations = input.ops | |
| newImage := &config.Image{&compute.Image{Name: "test-name", Family: "test-family"}, "test-project"} | |
| if err := deprecateInFamily(ctx, client, newImage, 0, fakeTime(date)); err != nil { | |
| t.Logf("input: %v", input) | |
| t.Logf("deprecateInFamily(_, _, %v, 0, _)", newImage) | |
| t.Fatal(err) | |
| } | |
| if len(fakeGCE.Deprecated) != len(input.images) { | |
| t.Fatalf("deprecated: %v actual: %d expected: %d", fakeGCE.Deprecated, len(fakeGCE.Deprecated), | |
| len(input.images)) | |
| } | |
| for _, image := range input.images { | |
| status := fakeGCE.Deprecated[image.Name] | |
| if status.State != "DEPRECATED" { | |
| t.Errorf("image: %v actual: %s expected: DEPRECATED", image, status.State) | |
| } | |
| if want := newImage.URL(); status.Replacement != want { | |
| t.Errorf("image: %v actual: %s expected: %s", image, status.Replacement, want) | |
| } | |
| } | |
| }) | |
| } | |
| } | |
| func TestDeprecateInFamilyTimeout(t *testing.T) { | |
| date := time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC) | |
| fakeGCE, client := fakes.GCEForTest(t, "test-project") | |
| defer fakeGCE.Close() | |
| ctx := context.Background() | |
| fakeGCE.Images.Items = []*compute.Image{{Name: "dep-1"}} | |
| fakeGCE.Operations = nil | |
| for i := 0; i < 1000; i++ { | |
| fakeGCE.Operations = append(fakeGCE.Operations, &compute.Operation{Name: "", Status: "RUNNING"}) | |
| } | |
| newImage := &config.Image{&compute.Image{Name: "test-name", Family: "test-family"}, "test-project"} | |
| if err := deprecateInFamily(ctx, client, newImage, 0, fakeTime(date)); err != ErrTimeout { | |
| t.Errorf("operation did not timeout. err: %s", err) | |
| } | |
| } | |
| func TestDeprecateInFamilyTTL(t *testing.T) { | |
| date := time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC) | |
| fakeGCE, client := fakes.GCEForTest(t, "test-project") | |
| defer fakeGCE.Close() | |
| ctx := context.Background() | |
| fakeGCE.Images.Items = []*compute.Image{{Name: "dep-1"}} | |
| fakeGCE.Operations = []*compute.Operation{{Name: "op-1", Status: "DONE"}} | |
| newImage := &config.Image{&compute.Image{Name: "test-name", Family: "test-family"}, "test-project"} | |
| if err := deprecateInFamily(ctx, client, newImage, 30, fakeTime(date)); err != nil { | |
| t.Logf("fakeGCE.Images.Items: %v", fakeGCE.Images.Items) | |
| t.Logf("fakeGCE.Operations: %v", fakeGCE.Operations) | |
| t.Logf("deprecateInFamily(_, _, %v, 30, _)", newImage) | |
| t.Fatal(err) | |
| } | |
| status := fakeGCE.Deprecated["dep-1"] | |
| expected := date.Add(time.Duration(30) * time.Second).Format(time.RFC3339) | |
| if status.Deleted != expected { | |
| t.Errorf("actual: %s expected: %s", status.Deleted, expected) | |
| } | |
| } | |
| func TestImageExists(t *testing.T) { | |
| testImageExistsData := []struct { | |
| testName string | |
| images []*compute.Image | |
| name string | |
| expected bool | |
| }{ | |
| { | |
| "DoesntExist", | |
| []*compute.Image{{Name: "im-1"}}, | |
| "im-2", | |
| false, | |
| }, | |
| { | |
| "Exists", | |
| []*compute.Image{{Name: "im-1"}}, | |
| "im-1", | |
| true, | |
| }, | |
| } | |
| fakeGCE, client := fakes.GCEForTest(t, "test-project") | |
| defer fakeGCE.Close() | |
| for _, input := range testImageExistsData { | |
| t.Run(input.testName, func(t *testing.T) { | |
| fakeGCE.Images.Items = input.images | |
| actual, err := ImageExists(client, "test-project", input.name) | |
| if err != nil { | |
| t.Logf("ImageExists(_, _, %s): %t", input.name, actual) | |
| t.Logf("fakeGCE.Images.Items: %v", fakeGCE.Images.Items) | |
| t.Fatal(err) | |
| } | |
| if actual != input.expected { | |
| t.Logf("ImageExists(_, _, %s): %t", input.name, actual) | |
| t.Logf("fakeGCE.Images.Items: %v", fakeGCE.Images.Items) | |
| t.Errorf("actual: %v expected: %v", actual, input.expected) | |
| } | |
| }) | |
| } | |
| } | |
| func buildImageList(names []string) []*compute.Image { | |
| var images []*compute.Image | |
| for _, name := range names { | |
| images = append(images, &compute.Image{Name: name}) | |
| } | |
| return images | |
| } | |
| func TestResolveMilestone(t *testing.T) { | |
| testResolveMilestoneData := []struct { | |
| testName string | |
| names []string | |
| milestone int | |
| expected string | |
| expectedError error | |
| }{ | |
| { | |
| "OneCandidate", | |
| []string{"cos-dev-68-10718-0-0", "cos-beta-67-10525-0-0"}, | |
| 68, | |
| "cos-dev-68-10718-0-0", | |
| nil, | |
| }, | |
| { | |
| "TwoCandidates", | |
| []string{"cos-dev-68-10718-11-0", "cos-dev-68-10718-0-0", "bad-image"}, | |
| 68, | |
| "cos-dev-68-10718-11-0", | |
| nil, | |
| }, | |
| { | |
| "NoImages", | |
| nil, | |
| 68, | |
| "", | |
| ErrImageNotFound, | |
| }, | |
| { | |
| "NoCandidates", | |
| []string{"bad-image"}, | |
| 68, | |
| "", | |
| ErrImageNotFound, | |
| }, | |
| } | |
| fakeGCE, client := fakes.GCEForTest(t, "cos-cloud") | |
| defer fakeGCE.Close() | |
| for _, input := range testResolveMilestoneData { | |
| t.Run(input.testName, func(t *testing.T) { | |
| ctx := context.Background() | |
| fakeGCE.Images.Items = buildImageList(input.names) | |
| actual, err := ResolveMilestone(ctx, client, input.milestone) | |
| if err != input.expectedError { | |
| t.Errorf("ResolveMilestone(_, _, %v) = %s, want: %s", input.milestone, err, input.expectedError) | |
| } | |
| if actual != input.expected { | |
| t.Errorf("ResolveMilestone(_, _, %v) = %s, want: %s", input.milestone, actual, input.expected) | |
| } | |
| }) | |
| } | |
| } |