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 }