blob: 082afdbc0725b33f7c9b5faf6f5215d23cacffda [file] [log] [blame]
// 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 main
import (
"context"
"flag"
"fmt"
"io/ioutil"
"os"
"testing"
"github.com/GoogleCloudPlatform/cos-customizer/src/pkg/config"
"github.com/GoogleCloudPlatform/cos-customizer/src/pkg/fakes"
"github.com/GoogleCloudPlatform/cos-customizer/src/pkg/fs"
"cloud.google.com/go/storage"
"github.com/google/subcommands"
compute "google.golang.org/api/compute/v1"
)
func executeFinishBuild(files *fs.Files, svc *compute.Service, gcs *storage.Client, flags ...string) (subcommands.ExitStatus, error) {
clients := ServiceClients(func(_ context.Context, _ bool) (*compute.Service, *storage.Client, error) {
return svc, gcs, nil
})
flagSet := &flag.FlagSet{}
finishBuild := &FinishImageBuild{}
finishBuild.SetFlags(flagSet)
if err := flagSet.Parse(flags); err != nil {
return 0, err
}
ret := finishBuild.Execute(context.Background(), flagSet, files, clients)
if ret != subcommands.ExitSuccess {
return ret, fmt.Errorf("FinishImageBuild failed; input: %v", flags)
}
return ret, nil
}
func setupFinishBuildFiles() (string, *fs.Files, error) {
tmpDir, err := ioutil.TempDir("", "")
if err != nil {
return "", nil, err
}
files := &fs.Files{}
files.DaisyWorkflow, err = createTempFile(tmpDir)
if err != nil {
os.RemoveAll(tmpDir)
return "", nil, err
}
buildConfigFile, err := ioutil.TempFile(tmpDir, "")
if err != nil {
os.RemoveAll(tmpDir)
return "", nil, err
}
if err := config.SaveConfigToFile(buildConfigFile, &config.Build{GCSBucket: "b", GCSDir: "d"}); err != nil {
buildConfigFile.Close()
os.RemoveAll(tmpDir)
return "", nil, err
}
if err := buildConfigFile.Close(); err != nil {
os.RemoveAll(tmpDir)
return "", nil, err
}
files.BuildConfig = buildConfigFile.Name()
files.ProvConfig, err = createTempFile(tmpDir)
if err != nil {
os.RemoveAll(tmpDir)
return "", nil, err
}
if err := ioutil.WriteFile(files.ProvConfig, []byte("{}"), 0644); err != nil {
os.RemoveAll(tmpDir)
return "", nil, err
}
sourceImageFile, err := ioutil.TempFile(tmpDir, "")
if err != nil {
os.RemoveAll(tmpDir)
return "", nil, err
}
if err := config.Save(sourceImageFile, config.NewImage("in", "p")); err != nil {
sourceImageFile.Close()
os.RemoveAll(tmpDir)
return "", nil, err
}
if err := sourceImageFile.Close(); err != nil {
os.RemoveAll(tmpDir)
return "", nil, err
}
files.SourceImageConfig = sourceImageFile.Name()
files.DaisyBin = "/bin/true"
files.UserBuildContextArchive, err = createTempFile(tmpDir)
if err != nil {
os.RemoveAll(tmpDir)
return "", nil, err
}
return tmpDir, files, nil
}
func TestOutputImageExists(t *testing.T) {
tmpDir, files, err := setupFinishBuildFiles()
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpDir)
gcs := fakes.GCSForTest(t)
gce, svc := fakes.GCEForTest(t, "p")
gce.Images = &compute.ImageList{Items: []*compute.Image{{Name: "out"}}}
files.DaisyBin = "/bin/false"
if _, err := executeFinishBuild(files, svc, gcs.Client, "-project=p", "-zone=z", "-image-name=out", "-image-project=p"); err != nil {
t.Logf("images: %v", gce.Images)
t.Errorf("FinishImageBuild.Execute(-image-name=out -image-project=p); daisy shouldn't execute if image exists; err: %q", err)
}
}
func TestOutputImageSuffixExists(t *testing.T) {
tmpDir, files, err := setupFinishBuildFiles()
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpDir)
gcs := fakes.GCSForTest(t)
gce, svc := fakes.GCEForTest(t, "p")
gce.Images = &compute.ImageList{Items: []*compute.Image{{Name: "in-out"}}}
files.DaisyBin = "/bin/false"
if _, err := executeFinishBuild(files, svc, gcs.Client, "-project=p", "-zone=z", "-image-suffix=-out", "-image-project=p"); err != nil {
t.Logf("images: %v", gce.Images)
t.Errorf("FinishImageBuild.Execute(-image-suffix=-out -image-project=p); daisy shouldn't execute if image exists; err: %q", err)
}
}
func TestDeprecateImages(t *testing.T) {
tmpDir, files, err := setupFinishBuildFiles()
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpDir)
gcs := fakes.GCSForTest(t)
gce, svc := fakes.GCEForTest(t, "p")
gce.Images = &compute.ImageList{Items: []*compute.Image{{Name: "old", Family: "f"}}}
gce.Operations = []*compute.Operation{{Status: "DONE"}}
if _, err := executeFinishBuild(files, svc, gcs.Client, "-project=p", "-zone=z", "-image-name=out", "-image-project=p", "-image-family=f", "-deprecate-old-images"); err != nil {
t.Fatal(err)
}
if _, ok := gce.Deprecated["old"]; !ok {
t.Errorf("Image 'old' is not deprecated; deprecated images: %v", gce.Deprecated)
}
}
func TestValidateFailure(t *testing.T) {
tests := []struct {
name string
flags []string
expectErr bool
msg string
}{
{
name: "Timeout",
flags: []string{"-project=p", "-zone=z", "-image-name=out", "-image-project=p", "-image-family=f", "-timeout=t"},
expectErr: true,
msg: "'timeout' value should be invalid",
}, {
name: "SmallDiskSize",
flags: []string{"-project=p", "-zone=z", "-image-name=out", "-image-project=p", "-image-family=f", "-disk-size-gb=11", "-oem-size=3072M"},
expectErr: true,
msg: "disk size should be invalid",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
tmpDir, files, err := setupFinishBuildFiles()
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpDir)
gcs := fakes.GCSForTest(t)
_, svc := fakes.GCEForTest(t, "p")
if _, err := executeFinishBuild(files, svc, gcs.Client, test.flags...); test.expectErr && err == nil {
t.Errorf("Got nil, want error; %s", test.msg)
}
})
}
}