github.com/GoogleCloudPlatform/compute-image-tools/cli_tools@v0.0.0-20240516224744-de2dabc4ed1b/common/utils/param/network_resolver.go (about)

     1  //  Copyright 2021 Google Inc. All Rights Reserved.
     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 param
    16  
    17  import (
    18  	"strings"
    19  
    20  	daisy "github.com/GoogleCloudPlatform/compute-daisy"
    21  	daisyCompute "github.com/GoogleCloudPlatform/compute-daisy/compute"
    22  	"google.golang.org/api/compute/v1"
    23  
    24  	"github.com/GoogleCloudPlatform/compute-image-tools/cli_tools/common/utils/paramhelper"
    25  )
    26  
    27  // NetworkResolver standardizes and validates network and subnet fields. It follows the
    28  // rules from the `networkInterfaces[].network` section of instances.insert:
    29  //
    30  //	  https://cloud.google.com/compute/docs/reference/rest/v1/instances/insert
    31  //
    32  //	- When both subnet and network are empty, explicitly use the default network.
    33  //	- If the subnet is empty, leave it empty to allow the compute backend to infer it.
    34  //	- Similarly, if only the network is empty, leave it empty to allow inference.
    35  //	- Backfill project and region resource properties if they are omitted in the network and subnet URIs.
    36  type NetworkResolver interface {
    37  	// Resolve returns the URI representation of network and subnet
    38  	// within a given region.
    39  	//
    40  	// There are two goals:
    41  	//
    42  	//    a. Explicitly use the 'default' network only when
    43  	//       network is omitted and subnet is empty.
    44  	//    b. Convert bare identifiers to URIs.
    45  	Resolve(originalNetwork, originalSubnet, region, project string) (network, subnet string, err error)
    46  }
    47  
    48  // NewNetworkResolver returns a NetworkResolver implementation that uses the Compute API.
    49  func NewNetworkResolver(client daisyCompute.Client) NetworkResolver {
    50  	return &computeNetworkResolver{client}
    51  }
    52  
    53  // computeNetworkResolver uses the Compute API to implement NetworkResolver.
    54  type computeNetworkResolver struct {
    55  	client daisyCompute.Client
    56  }
    57  
    58  func (r *computeNetworkResolver) Resolve(
    59  	originalNetwork, originalSubnet, region, project string) (network, subnet string, err error) {
    60  
    61  	// 1. Segment the user's input into component fields such as network name, subnet name, project, and region.
    62  	// If the URI in originalNetwork or originalSubnet didn't specify project or region, then backfill
    63  	// those fields using region and project.
    64  	networkResource, subnetResource, err := parseNetworkAndSubnet(originalNetwork, originalSubnet, region, project)
    65  	if err != nil {
    66  		return "", "", err
    67  	}
    68  
    69  	if networkResource.String() == "" && subnetResource.String() == "" {
    70  		return "", "", nil
    71  	}
    72  
    73  	// 2. Query the Compute API to check whether the network and subnet exist.
    74  	var subnetResponse *compute.Subnetwork
    75  	var networkResponse *compute.Network
    76  	if subnetResource.String() != "" {
    77  		subnetResponse, err = r.client.GetSubnetwork(subnetResource.Project, subnetResource.Region, subnetResource.Name)
    78  		if err != nil {
    79  			return "", "", daisy.Errf("Validation of subnetwork %q failed: %s", subnetResource, err)
    80  		}
    81  	}
    82  	if networkResource.String() != "" {
    83  		networkResponse, err = r.client.GetNetwork(networkResource.Project, networkResource.Name)
    84  		if err != nil {
    85  			return "", "", daisy.Errf("Validation of network %q failed: %s", networkResource, err)
    86  		}
    87  	}
    88  
    89  	// 3. Check whether the subnet's network matches the user's specified network.
    90  	if subnetResponse != nil && networkResponse != nil && subnetResponse.Network != networkResponse.SelfLink {
    91  		return "", "", daisy.Errf("Network %q does not contain subnet %q", networkResource, subnetResource)
    92  	}
    93  	return networkResource.String(), subnetResource.String(), err
    94  }
    95  
    96  // parseNetworkAndSubnet parses the user's values into structs and backfills
    97  // missing fields based on other values provided by the user.
    98  func parseNetworkAndSubnet(originalNetwork, originalSubnet, region, project string) (
    99  	*paramhelper.NetworkResource, *paramhelper.SubnetResource, error) {
   100  
   101  	networkResource, err := paramhelper.SplitNetworkResource(strings.TrimSpace(originalNetwork))
   102  	if err != nil {
   103  		return nil, nil, err
   104  	}
   105  	subnetResource, err := paramhelper.SplitSubnetResource(strings.TrimSpace(originalSubnet))
   106  	if err != nil {
   107  		return nil, nil, err
   108  	}
   109  	if networkResource.String() == "" && subnetResource.String() == "" {
   110  		return &paramhelper.NetworkResource{
   111  			Name:    "default",
   112  			Project: project,
   113  		}, &paramhelper.SubnetResource{}, nil
   114  	}
   115  	if networkResource.String() != "" {
   116  		if networkResource.Project == "" {
   117  			networkResource.Project = project
   118  		}
   119  	}
   120  	if subnetResource.String() != "" {
   121  		if subnetResource.Project == "" {
   122  			subnetResource.Project = project
   123  		}
   124  		if subnetResource.Region == "" {
   125  			subnetResource.Region = region
   126  		}
   127  	}
   128  	return networkResource, subnetResource, nil
   129  }