github.com/crossplane-contrib/function-cue@v0.2.2-0.20240508161918-5100fcb5a058/internal/fn/debug.go (about)

     1  // Licensed to Elasticsearch B.V. under one or more contributor
     2  // license agreements. See the NOTICE file distributed with
     3  // this work for additional information regarding copyright
     4  // ownership. Elasticsearch B.V. licenses this file to you under
     5  // the Apache License, Version 2.0 (the "License"); you may
     6  // not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing,
    12  // software distributed under the License is distributed on an
    13  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14  // KIND, either express or implied.  See the License for the
    15  // specific language governing permissions and limitations
    16  // under the License.
    17  
    18  package fn
    19  
    20  import (
    21  	"encoding/json"
    22  	"fmt"
    23  
    24  	"cuelang.org/go/cue/format"
    25  )
    26  
    27  const connectionDetailsKey = "connectionDetails"
    28  
    29  // start debugging routines
    30  
    31  // noise that people typically wouldn't want to see when looking at inputs.
    32  var systemAttributes = []struct{ parent, name string }{
    33  	{"annotations", "kubectl.kubernetes.io/last-applied-configuration"},
    34  	{"metadata", "managedFields"},
    35  	{"metadata", "creationTimestamp"},
    36  	{"metadata", "generation"},
    37  	{"metadata", "resourceVersion"},
    38  	{"metadata", "uid"},
    39  }
    40  
    41  var systemAttrsLookup map[string]map[string]bool
    42  
    43  // init reformulates system attributes as a map for easier lookup.
    44  func init() {
    45  	systemAttrsLookup = map[string]map[string]bool{}
    46  	for _, attr := range systemAttributes {
    47  		attrMap, ok := systemAttrsLookup[attr.parent]
    48  		if !ok {
    49  			attrMap = map[string]bool{}
    50  			systemAttrsLookup[attr.parent] = attrMap
    51  		}
    52  		attrMap[attr.name] = true
    53  	}
    54  }
    55  
    56  // walkDelete performs a recursive walk on the supplied object to remove system generated
    57  // attributes.
    58  func walkDelete(input any, parent string) {
    59  	switch input := input.(type) {
    60  	case []any:
    61  		for _, v := range input {
    62  			walkDelete(v, parent)
    63  		}
    64  	case map[string]any:
    65  		if parent == connectionDetailsKey {
    66  			for k := range input {
    67  				input[k] = []byte("<redacted>")
    68  			}
    69  		}
    70  		attrMap := systemAttrsLookup[parent]
    71  		for k, v := range input {
    72  			if attrMap != nil && attrMap[k] {
    73  				delete(input, k)
    74  				continue
    75  			}
    76  			walkDelete(v, k)
    77  		}
    78  	}
    79  }
    80  
    81  func (f *Cue) reserialize(jsonBytes []byte, raw bool) ([]byte, error) {
    82  	var input any
    83  	err := json.Unmarshal(jsonBytes, &input)
    84  	if err != nil {
    85  		f.log.Info(fmt.Sprintf("JSON unmarshal error: %v", err))
    86  		return jsonBytes, err
    87  	}
    88  	if !raw {
    89  		walkDelete(input, "")
    90  	}
    91  	b, err := json.MarshalIndent(input, "", "  ")
    92  	if err != nil {
    93  		f.log.Info(fmt.Sprintf("JSON marshal error: %v", err))
    94  		return jsonBytes, err
    95  	}
    96  	return b, nil
    97  }
    98  
    99  // getDebugString modifies the supplied JSON bytes to remove k8s and crossplane generated metadata
   100  // and returns its serialized form as a formatted cue object for a better user experience.
   101  // In case of any errors, it returns the input bytes as a string.
   102  func (f *Cue) getDebugString(jsonBytes []byte, raw bool) string {
   103  	var err error
   104  	jsonBytes, err = f.reserialize(jsonBytes, raw)
   105  	if err != nil {
   106  		return string(jsonBytes)
   107  	}
   108  	out, err := format.Source(jsonBytes, format.Simplify(), format.TabIndent(false), format.UseSpaces(2))
   109  	if err != nil {
   110  		f.log.Info(fmt.Sprintf("cue formatting error: %v", err))
   111  		return string(jsonBytes)
   112  	}
   113  	return string(out)
   114  }