...

Source file src/github.com/GoogleCloudPlatform/k8s-config-connector/pkg/resourceskeleton/uri/uri.go

Documentation: github.com/GoogleCloudPlatform/k8s-config-connector/pkg/resourceskeleton/uri

     1  // Copyright 2022 Google LLC
     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 uri
    16  
    17  import (
    18  	"fmt"
    19  	"regexp"
    20  
    21  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/core/v1alpha1"
    22  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/servicemapping/servicemappingloader"
    23  )
    24  
    25  func GetServiceMappingAndResourceConfig(smLoader *servicemappingloader.ServiceMappingLoader, urlHost, urlPath string) (*v1alpha1.ServiceMapping, *v1alpha1.ResourceConfig, error) {
    26  	sm, err := smLoader.GetServiceMappingForServiceHostName(urlHost)
    27  	if err != nil {
    28  		return nil, nil, fmt.Errorf("error getting service mapping: %v", err)
    29  	}
    30  	rc, err := matchResourceNameToRC(urlPath, sm)
    31  	if err != nil {
    32  		return nil, nil, err
    33  	}
    34  	return sm, rc, err
    35  }
    36  
    37  func matchResourceNameToRC(uriPath string, sm *v1alpha1.ServiceMapping) (*v1alpha1.ResourceConfig, error) {
    38  	if isHierarchicalResource(sm, uriPath) {
    39  		return matchResourceNameToHierarchalRC(uriPath, sm)
    40  	}
    41  	return matchResourceNameToRCGeneral(uriPath, sm)
    42  }
    43  
    44  func matchResourceNameToRCGeneral(uriPath string, sm *v1alpha1.ServiceMapping) (*v1alpha1.ResourceConfig, error) {
    45  	for _, rc := range sm.Spec.Resources {
    46  		if !*rc.IDTemplateCanBeUsedToMatchResourceName {
    47  			continue
    48  		}
    49  		// resources that skip import often have id templates that are broad and match too many resources, examples:
    50  		// * google_compute_network_peering
    51  		if rc.SkipImport {
    52  			continue
    53  		}
    54  		regexIDTemplate := idTemplateToRegex(rc.IDTemplate)
    55  		regex, err := regexp.Compile(regexIDTemplate)
    56  		if err != nil {
    57  			return nil, fmt.Errorf("error compiling '%v' to regex: %v", regexIDTemplate, err)
    58  		}
    59  		if regex.MatchString(uriPath) {
    60  			return &rc, nil
    61  		}
    62  	}
    63  	return nil, fmt.Errorf("unable to find a matching resource config for uri path '%v'", uriPath)
    64  }
    65  
    66  func matchResourceNameToHierarchalRC(urlPath string, sm *v1alpha1.ServiceMapping) (*v1alpha1.ResourceConfig, error) {
    67  	resourceName := getHierarchicalResourceName(urlPath)
    68  	for _, rc := range sm.Spec.Resources {
    69  		if rc.Kind == resourceName {
    70  			return &rc, nil
    71  		}
    72  	}
    73  	return nil, fmt.Errorf("unable to find a hierarchal resource with name '%v' in service mapping '%v'", resourceName, sm.Spec.Name)
    74  }
    75  
    76  var replaceRegex = regexp.MustCompile("(^|/){{.*?}}")
    77  
    78  // idTemplateToRegex replaces all fields in the id template that are 'user input' with a wildcard that matches everything except
    79  // the '/' character, i.e. the id template,
    80  //
    81  //	projects/{{project}}/regions/{{region}}/forwardingRules/{{name}}
    82  //
    83  // becomes
    84  //
    85  //	projects/[^/]*/regions/[^/]*/forwardingRules/[^/]*
    86  //
    87  // the expression [^/] is a negative match, meaning, match everything that is not a '/'
    88  func idTemplateToRegex(idTemplate string) string {
    89  	// add ($|\?.*) to the end of the id template to ensure that the string we are matching 'ends' with either the
    90  	// last portion of the id template *or* has a query param (the ?). This is to prevent id templates which are a
    91  	// substring of another id template from matching each other.
    92  	//
    93  	// Example, BigQueryDataset's id template is a substring of BigQueryTable's id template.
    94  	// * BigQueryDataset: projects/{{project}}/datasets/{{dataset_id}}
    95  	// * BigQueryTable:   projects/{{project}}/datasets/{{dataset_id}}/tables/{{table_id}}
    96  	idTemplate = fmt.Sprintf("%v($|\\?.*)", idTemplate)
    97  	return replaceRegex.ReplaceAllString(idTemplate, "$1[^/]*")
    98  }
    99  
   100  var (
   101  	projectsURLRegex = regexp.MustCompile("(/.*)?/projects/.*")
   102  	foldersURLRegex  = regexp.MustCompile("(/.*)?/folders/.*")
   103  )
   104  
   105  func getHierarchicalResourceName(urlPath string) string {
   106  	if foldersURLRegex.MatchString(urlPath) {
   107  		return "Folder"
   108  	}
   109  	return "Project"
   110  }
   111  
   112  func isHierarchicalResource(sm *v1alpha1.ServiceMapping, urlPath string) bool {
   113  	if sm.Spec.Name != "ResourceManager" {
   114  		return false
   115  	}
   116  	if projectsURLRegex.MatchString(urlPath) {
   117  		return true
   118  	}
   119  	if foldersURLRegex.MatchString(urlPath) {
   120  		return true
   121  	}
   122  	return false
   123  }
   124  

View as plain text