blob: 42a581e58680486d3e2b555a494b2f502c7447bb [file] [log] [blame]
// Copyright 2018 The LUCI Authors.
//
// 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"
"fmt"
"strings"
"time"
"google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"
"github.com/maruel/subcommands"
"go.chromium.org/luci/common/cli"
"go.chromium.org/luci/common/errors"
"go.chromium.org/luci/common/flag"
"go.chromium.org/luci/common/logging"
)
// createSnapshotCmd is the command to create a disk snapshot.
type createSnapshotCmd struct {
cmdRunBase
// disk is the name of the disk to create a snapshot of.
disk string
// labels is a slice of strings in key:value form to attach to the snapshot as labels.
labels []string
// name is the name to give the created snapshot.
name string
// project is the name of the project where the disk exists, and to create the snapshot in.
project string
// zone is the name of the zone where the disk exists.
zone string
}
// validateFlags validates parsed command line flags and returns a map of parsed labels.
func (cmd *createSnapshotCmd) validateFlags(c context.Context) (map[string]string, error) {
if cmd.disk == "" {
return nil, errors.New("-disk is required")
}
labels := make(map[string]string, len(cmd.labels))
for _, label := range cmd.labels {
parts := strings.SplitN(label, ":", 2)
if len(parts) != 2 {
return nil, errors.New(fmt.Sprintf("-label %q must be in key:value form", label))
}
if _, ok := labels[parts[0]]; ok {
return nil, errors.New(fmt.Sprintf("-label %q has duplicate key", label))
}
labels[parts[0]] = parts[1]
}
if cmd.name == "" {
return nil, errors.New("-name is required")
}
if cmd.project == "" {
return nil, errors.New("-project is required")
}
if cmd.zone == "" {
return nil, errors.New("-zone is required")
}
return labels, nil
}
// Run runs the command to create a disk snapshot.
func (cmd *createSnapshotCmd) Run(app subcommands.Application, args []string, env subcommands.Env) int {
c := cli.GetContext(app, cmd, env)
labels, err := cmd.validateFlags(c)
if err != nil {
logging.Errorf(c, "%s", err.Error())
return 1
}
srv := getService(c)
logging.Infof(c, "Creating snapshot.")
snapshot := &compute.Snapshot{
Labels: labels,
Name: cmd.name,
}
op, err := compute.NewDisksService(srv).CreateSnapshot(cmd.project, cmd.zone, cmd.disk, snapshot).Context(c).Do()
for {
if err != nil {
for _, err := range err.(*googleapi.Error).Errors {
logging.Errorf(c, "%s", err.Message)
}
return 1
}
if op.Status == "DONE" {
break
}
logging.Infof(c, "Waiting for snapshot to be created...")
time.Sleep(2 * time.Second)
op, err = compute.NewZoneOperationsService(srv).Get(cmd.project, cmd.zone, op.Name).Context(c).Do()
}
if op.Error != nil {
for _, err := range op.Error.Errors {
logging.Errorf(c, "%s", err.Message)
}
return 1
}
logging.Infof(c, "Created snapshot.")
return 0
}
// getCreateSnapshotCmd returns a new command to create a snapshot.
func getCreateSnapshotCmd() *subcommands.Command {
return &subcommands.Command{
UsageLine: "create -disk <disk> -name <name> -project <project> -zone <zone> [-label <key>:<value>]...",
ShortDesc: "creates a snapshot",
LongDesc: "Creates a disk snapshot.",
CommandRun: func() subcommands.CommandRun {
cmd := &createSnapshotCmd{}
cmd.Initialize()
cmd.Flags.StringVar(&cmd.disk, "disk", "", "Disk to create a snapshot of.")
cmd.Flags.Var(flag.StringSlice(&cmd.labels), "label", "Label to attach to a snapshot.")
cmd.Flags.StringVar(&cmd.name, "name", "", "Name to give the created snapshot.")
cmd.Flags.StringVar(&cmd.project, "project", "", "Project where the disk exists, and to create the snapshot in.")
cmd.Flags.StringVar(&cmd.zone, "zone", "", "Zone where the disk exists.")
return cmd
},
}
}