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

     1  // Copyright 2019 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  	"go.chromium.org/luci/config/validation"
    21  )
    22  
    23  // isValidImage returns whether or not the given string is a valid image name.
    24  // Image names must have the form projects/<project>/global/images/<image> or
    25  // global/images/<image>.
    26  func isValidImage(s string) bool {
    27  	switch parts := strings.Split(s, "/"); len(parts) {
    28  	case 3:
    29  		return parts[0] == "global" && parts[1] == "images"
    30  	case 5:
    31  		return parts[0] == "projects" && parts[2] == "global" && parts[3] == "images"
    32  	}
    33  	return false
    34  }
    35  
    36  // IsPersistentDisk returns whether or not the given string is a persistent
    37  // disk type.
    38  func (d *Disk) IsPersistentDisk() bool {
    39  	return strings.HasSuffix(d.Type, "/pd-standard") || strings.HasSuffix(d.Type, "/pd-ssd")
    40  }
    41  
    42  // IsScratchDisk returns whether or not the given string is a scratch disk
    43  // type.
    44  func (d *Disk) IsScratchDisk() bool {
    45  	return strings.HasSuffix(d.Type, "/local-ssd")
    46  }
    47  
    48  // isValidDiskType returns whether or not the given string is a valid disk
    49  // type. Disk types have the form zones/<zone>/diskTypes/<type>.
    50  func isValidDiskType(s string) bool {
    51  	// Empty disk type implies the default.
    52  	if s == "" {
    53  		return true
    54  	}
    55  	parts := strings.Split(s, "/")
    56  	return len(parts) == 4 && parts[0] == "zones" && parts[2] == "diskTypes"
    57  }
    58  
    59  // GetImageBase returns the base image name for this validated disk.
    60  func (d *Disk) GetImageBase() string {
    61  	return d.GetImage()[strings.LastIndex(d.GetImage(), "/")+1:]
    62  }
    63  
    64  // Validate validates this disk.
    65  //
    66  //	The set of valid configurations is:
    67  //	+-------------+-------+-----------+
    68  //	| Type        | Image | Interface |
    69  //	+-------------+-------+-----------+
    70  //	| local-ssd   | No    | *         |
    71  //	| pd-ssd      | Yes   | SCSI      |
    72  //	| pd-standard | Yes   | SCSI      |
    73  //	+-------------+-------+-----------+
    74  func (d *Disk) Validate(c *validation.Context) {
    75  	if !isValidDiskType(d.Type) {
    76  		c.Errorf("disk type must match zones/<zone>/diskTypes/<type>")
    77  	}
    78  	if d.IsPersistentDisk() && d.GetInterface() != DiskInterface_SCSI {
    79  		c.Errorf("persistent disk must use SCSI")
    80  	}
    81  	if d.IsPersistentDisk() && !isValidImage(d.GetImage()) {
    82  		c.Errorf("image must match projects/<project>/global/images/<image> or global/images/<image>")
    83  	}
    84  	if d.IsScratchDisk() && isValidImage(d.GetImage()) {
    85  		c.Errorf("local ssd cannot use an image")
    86  	}
    87  }