go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gce/api/config/v1/vm.go (about)

     1  // Copyright 2018 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package config
    16  
    17  import (
    18  	"strings"
    19  
    20  	"google.golang.org/protobuf/proto"
    21  
    22  	"go.chromium.org/luci/config/validation"
    23  	"go.chromium.org/luci/gae/service/datastore"
    24  )
    25  
    26  // Ensure VM implements datastore.PropertyConverter.
    27  // This allows VMs to be read from and written to the datastore.
    28  var _ datastore.PropertyConverter = &VM{}
    29  
    30  // FromProperty implements datastore.PropertyConverter.
    31  func (v *VM) FromProperty(p datastore.Property) error {
    32  	if p.Value() == nil {
    33  		v = &VM{}
    34  		return nil
    35  	}
    36  	return proto.Unmarshal(p.Value().([]byte), v)
    37  }
    38  
    39  // ToProperty implements datastore.PropertyConverter.
    40  func (v *VM) ToProperty() (datastore.Property, error) {
    41  	p := datastore.Property{}
    42  	bytes, err := proto.Marshal(v)
    43  	if err != nil {
    44  		return datastore.Property{}, err
    45  	}
    46  	// noindex is not respected in the tags in the model.
    47  	return p, p.SetValue(bytes, datastore.NoIndex)
    48  }
    49  
    50  // SetZone sets the given zone throughout this VM.
    51  func (v *VM) SetZone(zone string) {
    52  	for _, disk := range v.GetDisk() {
    53  		disk.Type = strings.Replace(disk.Type, "{{.Zone}}", zone, -1)
    54  	}
    55  	v.MachineType = strings.Replace(v.GetMachineType(), "{{.Zone}}", zone, -1)
    56  	v.Zone = zone
    57  }
    58  
    59  // Validate validates this VM description.
    60  // Metadata FromFile must already be converted to FromText.
    61  func (v *VM) Validate(c *validation.Context) {
    62  	if len(v.GetDisk()) == 0 {
    63  		c.Errorf("at least one disk is required")
    64  	}
    65  	for i, d := range v.GetDisk() {
    66  		c.Enter("disk %d", i)
    67  		d.Validate(c)
    68  		c.Exit()
    69  	}
    70  	if v.GetMachineType() == "" {
    71  		c.Errorf("machine type is required")
    72  	}
    73  	for i, meta := range v.GetMetadata() {
    74  		c.Enter("metadata %d", i)
    75  		// Implicitly rejects FromFile.
    76  		// FromFile must be converted to FromText before calling.
    77  		if !strings.Contains(meta.GetFromText(), ":") {
    78  			c.Errorf("metadata from text must be in key:value form")
    79  		}
    80  		c.Exit()
    81  	}
    82  	if len(v.GetNetworkInterface()) == 0 {
    83  		c.Errorf("at least one network interface is required")
    84  	}
    85  	if v.GetProject() == "" {
    86  		c.Errorf("project is required")
    87  	}
    88  	if v.GetZone() == "" {
    89  		c.Errorf("zone is required")
    90  	}
    91  }