...

Source file src/github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/extension/extension.go

Documentation: github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/extension

     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 extension is used to interpret the dcl extensions.
    16  package extension
    17  
    18  import (
    19  	"fmt"
    20  	"strings"
    21  
    22  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/constants"
    23  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/util/pathslice"
    24  
    25  	"github.com/nasa9084/go-openapi"
    26  )
    27  
    28  var (
    29  	trimmableReferenceSuffixes = []string{"Name", "Id", "IdOrNum", "Email", "Link", "Reference"}
    30  )
    31  
    32  // IsReferenceField takes the field schema and determines if the field is for resource reference.
    33  func IsReferenceField(schema *openapi.Schema) bool {
    34  	refSchema := schema
    35  	if schema.Type == "array" {
    36  		refSchema = schema.Items
    37  	}
    38  	_, ok := refSchema.Extension["x-dcl-references"]
    39  	return ok
    40  }
    41  
    42  // IsSensitiveField takes the field schema and determines if the field is sensitive.
    43  func IsSensitiveField(schema *openapi.Schema) (bool, error) {
    44  	val, ok := schema.Extension["x-dcl-sensitive"]
    45  	if !ok {
    46  		return false, nil
    47  	}
    48  	if schema.Type != "string" {
    49  		return false, fmt.Errorf("only support sensitive fields of `string` type, but got type %v", schema.Type)
    50  	}
    51  	boolVal, ok := val.(bool)
    52  	if !ok {
    53  		return false, fmt.Errorf("wrong type for 'x-dcl-sensitive' extension: %T, expect to have bool", val)
    54  	}
    55  	return boolVal, nil
    56  }
    57  
    58  func HasSensitiveFields(schema *openapi.Schema) (bool, error) {
    59  	sensitive, err := IsSensitiveField(schema)
    60  	if err != nil {
    61  		return false, err
    62  	}
    63  	if sensitive {
    64  		return true, nil
    65  	}
    66  
    67  	switch schema.Type {
    68  	case "array":
    69  		return HasSensitiveFields(schema.Items)
    70  	case "object":
    71  		if schema.AdditionalProperties != nil {
    72  			return HasSensitiveFields(schema.AdditionalProperties)
    73  		}
    74  		for _, fieldSchema := range schema.Properties {
    75  			sensitive, err = HasSensitiveFields(fieldSchema)
    76  			if err != nil {
    77  				return false, err
    78  			}
    79  			if sensitive {
    80  				return true, nil
    81  			}
    82  		}
    83  	}
    84  
    85  	return false, nil
    86  }
    87  
    88  func HasIam(schema *openapi.Schema) (bool, error) {
    89  	val, ok := schema.Extension["x-dcl-has-iam"]
    90  	if !ok { // extension doesn't exist
    91  		return false, nil
    92  	}
    93  	boolVal, ok := val.(bool)
    94  	if !ok {
    95  		return false, fmt.Errorf("wrong type for 'x-dcl-has-iam' extension: %T, expected to have bool", val)
    96  	}
    97  	return boolVal, nil
    98  }
    99  
   100  func IsImmutableField(schema *openapi.Schema) (bool, error) {
   101  	val, ok := schema.Extension["x-kubernetes-immutable"]
   102  	if !ok {
   103  		return false, nil
   104  	}
   105  	boolVal, ok := val.(bool)
   106  	if !ok {
   107  		return false, fmt.Errorf("wrong type for 'x-kubernetes-immutable' extension: %T, expect to have bool", val)
   108  	}
   109  	return boolVal, nil
   110  }
   111  
   112  func GetLabelsFieldSchema(schema *openapi.Schema) (labelsField string, fieldSchema *openapi.Schema, found bool, err error) {
   113  	raw, found := schema.Extension[constants.DCL_LABELS_FIELD]
   114  	if !found {
   115  		return "", nil, false, nil
   116  	}
   117  	labelsField, ok := raw.(string)
   118  	if !ok {
   119  		return "", nil, false, fmt.Errorf("wrong type for 'x-dcl-labels' extension: %T, expect to have string type", raw)
   120  	}
   121  	if labelsField == "" {
   122  		return "", nil, false, fmt.Errorf("'x-dcl-labels' field exists, but is an empty string")
   123  	}
   124  	path := strings.Split(labelsField, ".")
   125  	var ret *openapi.Schema
   126  	ret = schema
   127  	for _, field := range path {
   128  		if _, ok := ret.Properties[field]; !ok {
   129  			return "", nil, false, fmt.Errorf("couldn't find the schema for field %v", labelsField)
   130  		}
   131  		ret = ret.Properties[field]
   132  	}
   133  	return labelsField, ret, true, nil
   134  }
   135  
   136  // GetReferenceFieldName returns the converted field name given the original reference field name.
   137  func GetReferenceFieldName(path []string, schema *openapi.Schema) (string, error) {
   138  	field := pathslice.Base(path)
   139  	if len(path) == 1 && field == "parent" {
   140  		return "", fmt.Errorf("cannot get reference field name for 'parent' " +
   141  			"since 'parent' is typically split into multiple reference fields")
   142  	}
   143  	// If the filed is `array` type, it expects an list of references. We keep the original field name.
   144  	if schema.Type == "array" {
   145  		return field, nil
   146  	}
   147  	raw := schema.Extension["x-dcl-references"]
   148  	_, ok := raw.([]interface{})
   149  	if !ok {
   150  		return "", fmt.Errorf("wrong type for 'x-dcl-references' extension: %T, expect to have []interface{}", raw)
   151  	}
   152  	return formatReferenceFieldName(field), nil
   153  }
   154  
   155  func formatReferenceFieldName(fieldName string) string {
   156  	// If the original field name ends with one of the known suffixes, e.g. "xxxName",
   157  	// we want to convert it to "xxxRef" for consistency
   158  	for _, suffix := range trimmableReferenceSuffixes {
   159  		if strings.HasSuffix(fieldName, suffix) {
   160  			return strings.TrimSuffix(fieldName, suffix) + "Ref"
   161  		}
   162  	}
   163  	return fieldName + "Ref"
   164  }
   165  
   166  func GetNameFieldSchema(schema *openapi.Schema) (*openapi.Schema, bool) {
   167  	s, ok := schema.Properties["name"]
   168  	if !ok {
   169  		return nil, false
   170  	}
   171  	return s, true
   172  }
   173  
   174  func IsResourceIDFieldServerGenerated(nameFieldSchema *openapi.Schema) (bool, error) {
   175  	val, ok := nameFieldSchema.Extension["x-dcl-server-generated-parameter"]
   176  	if !ok {
   177  		return false, nil
   178  	}
   179  	boolVal, ok := val.(bool)
   180  	if !ok {
   181  		return false, fmt.Errorf("wrong type for 'x-dcl-server-generated-parameter' extension: %T, expect to have bool", val)
   182  	}
   183  	return boolVal, nil
   184  }
   185  
   186  func GetNameValueTemplate(schema *openapi.Schema) (string, error) {
   187  	raw, ok := schema.Extension["x-dcl-id"]
   188  	if !ok {
   189  		return "", fmt.Errorf("'x-dcl-id' is not found")
   190  	}
   191  	template, ok := raw.(string)
   192  	if !ok {
   193  		return "", fmt.Errorf("wrong type for 'x-dcl-id' extension: %T, expect to have string type", raw)
   194  	}
   195  	return template, nil
   196  }
   197  
   198  func HasStateHint(schema *openapi.Schema) (bool, error) {
   199  	val, ok := schema.Extension["x-dcl-uses-state-hint"]
   200  	if !ok {
   201  		return false, nil
   202  	}
   203  
   204  	boolVal, ok := val.(bool)
   205  	if !ok {
   206  		return false, fmt.Errorf("wrong type for 'x-dcl-uses-state-hint' extension: %T, expect to have bool type", val)
   207  	}
   208  
   209  	return boolVal, nil
   210  }
   211  
   212  func IsMutableButUnreadableField(schema *openapi.Schema) (bool, error) {
   213  	// Check if the field is unreadable.
   214  	val, ok := schema.Extension["x-dcl-mutable-unreadable"]
   215  	if !ok {
   216  		return false, nil
   217  	}
   218  
   219  	unreadable, ok := val.(bool)
   220  	if !ok {
   221  		return false, fmt.Errorf("wrong type for 'x-dcl-mutable-unreadable' extension: %T, expect to have bool type", val)
   222  	}
   223  
   224  	if !unreadable {
   225  		return false, nil
   226  	}
   227  
   228  	// Check if the field is mutable.
   229  	immutable, err := IsImmutableField(schema)
   230  	if err != nil {
   231  		return false, err
   232  	}
   233  	return !immutable, nil
   234  }
   235  

View as plain text