...

Source file src/k8s.io/kubectl/pkg/explain/v2/funcs.go

Documentation: k8s.io/kubectl/pkg/explain/v2

     1  /*
     2  Copyright 2022 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package v2
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"reflect"
    25  	"strings"
    26  	"text/template"
    27  
    28  	"github.com/go-openapi/jsonreference"
    29  	"k8s.io/kubectl/pkg/util/term"
    30  )
    31  
    32  type explainError string
    33  
    34  func (e explainError) Error() string {
    35  	return string(e)
    36  }
    37  
    38  func WithBuiltinTemplateFuncs(tmpl *template.Template) *template.Template {
    39  	return tmpl.Funcs(map[string]interface{}{
    40  		"throw": func(e string, args ...any) (string, error) {
    41  			errString := fmt.Sprintf(e, args...)
    42  			return "", explainError(errString)
    43  		},
    44  		"toJson": func(obj any) (string, error) {
    45  			res, err := json.Marshal(obj)
    46  			return string(res), err
    47  		},
    48  		"toPrettyJson": func(obj any) (string, error) {
    49  			res, err := json.MarshalIndent(obj, "", "    ")
    50  			if err != nil {
    51  				return "", err
    52  			}
    53  			return string(res), err
    54  		},
    55  		"fail": func(message string) (string, error) {
    56  			return "", errors.New(message)
    57  		},
    58  		"wrap": func(l int, s string) (string, error) {
    59  			buf := bytes.NewBuffer(nil)
    60  			writer := term.NewWordWrapWriter(buf, uint(l))
    61  			_, err := writer.Write([]byte(s))
    62  			if err != nil {
    63  				return "", err
    64  			}
    65  			return buf.String(), nil
    66  		},
    67  		"split": func(s string, sep string) []string {
    68  			return strings.Split(s, sep)
    69  		},
    70  		"join": func(sep string, strs ...string) string {
    71  			return strings.Join(strs, sep)
    72  		},
    73  		"include": func(name string, data interface{}) (string, error) {
    74  			buf := bytes.NewBuffer(nil)
    75  			if err := tmpl.ExecuteTemplate(buf, name, data); err != nil {
    76  				return "", err
    77  			}
    78  			return buf.String(), nil
    79  		},
    80  		"ternary": func(a, b any, condition bool) any {
    81  			if condition {
    82  				return a
    83  			}
    84  			return b
    85  		},
    86  		"first": func(list any) (any, error) {
    87  			if list == nil {
    88  				return nil, errors.New("list is empty")
    89  			}
    90  
    91  			tp := reflect.TypeOf(list).Kind()
    92  			switch tp {
    93  			case reflect.Slice, reflect.Array:
    94  				l2 := reflect.ValueOf(list)
    95  
    96  				l := l2.Len()
    97  				if l == 0 {
    98  					return nil, errors.New("list is empty")
    99  				}
   100  
   101  				return l2.Index(0).Interface(), nil
   102  			default:
   103  				return nil, fmt.Errorf("first cannot be used on type: %T", list)
   104  			}
   105  		},
   106  		"last": func(list any) (any, error) {
   107  			if list == nil {
   108  				return nil, errors.New("list is empty")
   109  			}
   110  
   111  			tp := reflect.TypeOf(list).Kind()
   112  			switch tp {
   113  			case reflect.Slice, reflect.Array:
   114  				l2 := reflect.ValueOf(list)
   115  
   116  				l := l2.Len()
   117  				if l == 0 {
   118  					return nil, errors.New("list is empty")
   119  				}
   120  
   121  				return l2.Index(l - 1).Interface(), nil
   122  			default:
   123  				return nil, fmt.Errorf("last cannot be used on type: %T", list)
   124  			}
   125  		},
   126  		"indent": func(amount int, str string) string {
   127  			pad := strings.Repeat(" ", amount)
   128  			return pad + strings.Replace(str, "\n", "\n"+pad, -1)
   129  		},
   130  		"dict": func(keysAndValues ...any) (map[string]any, error) {
   131  			if len(keysAndValues)%2 != 0 {
   132  				return nil, errors.New("expected even # of arguments")
   133  			}
   134  
   135  			res := map[string]any{}
   136  			for i := 0; i+1 < len(keysAndValues); i = i + 2 {
   137  				if key, ok := keysAndValues[i].(string); ok {
   138  					res[key] = keysAndValues[i+1]
   139  				} else {
   140  					return nil, fmt.Errorf("key of type %T is not a string as expected", key)
   141  				}
   142  			}
   143  
   144  			return res, nil
   145  		},
   146  		"contains": func(list any, value any) bool {
   147  			if list == nil {
   148  				return false
   149  			}
   150  
   151  			val := reflect.ValueOf(list)
   152  			switch val.Kind() {
   153  			case reflect.Array:
   154  			case reflect.Slice:
   155  				for i := 0; i < val.Len(); i++ {
   156  					cur := val.Index(i)
   157  					if cur.CanInterface() && reflect.DeepEqual(cur.Interface(), value) {
   158  						return true
   159  					}
   160  				}
   161  				return false
   162  			default:
   163  				return false
   164  			}
   165  			return false
   166  		},
   167  		"set": func(dict map[string]any, keysAndValues ...any) (any, error) {
   168  			if len(keysAndValues)%2 != 0 {
   169  				return nil, errors.New("expected even number of arguments")
   170  			}
   171  
   172  			copyDict := make(map[string]any, len(dict))
   173  			for k, v := range dict {
   174  				copyDict[k] = v
   175  			}
   176  
   177  			for i := 0; i < len(keysAndValues); i += 2 {
   178  				key, ok := keysAndValues[i].(string)
   179  				if !ok {
   180  					return nil, errors.New("keys must be strings")
   181  				}
   182  
   183  				copyDict[key] = keysAndValues[i+1]
   184  			}
   185  
   186  			return copyDict, nil
   187  		},
   188  		"list": func(values ...any) ([]any, error) {
   189  			return values, nil
   190  		},
   191  		"add": func(value, operand int) int {
   192  			return value + operand
   193  		},
   194  		"sub": func(value, operand int) int {
   195  			return value - operand
   196  		},
   197  		"mul": func(value, operand int) int {
   198  			return value * operand
   199  		},
   200  		"resolveRef": func(refAny any, document map[string]any) map[string]any {
   201  			refString, ok := refAny.(string)
   202  			if !ok {
   203  				// if passed nil, or wrong type just treat the same
   204  				// way as unresolved reference (makes for easier templates)
   205  				return nil
   206  			}
   207  
   208  			// Resolve field path encoded by the ref
   209  			ref, err := jsonreference.New(refString)
   210  			if err != nil {
   211  				// Unrecognized ref format.
   212  				return nil
   213  			}
   214  
   215  			if !ref.HasFragmentOnly {
   216  				// Downloading is not supported. Treat as not found
   217  				return nil
   218  			}
   219  
   220  			fragment := ref.GetURL().Fragment
   221  			components := strings.Split(fragment, "/")
   222  			cur := document
   223  
   224  			for _, k := range components {
   225  				if len(k) == 0 {
   226  					// first component is usually empty (#/components/) , etc
   227  					continue
   228  				}
   229  
   230  				next, ok := cur[k].(map[string]any)
   231  				if !ok {
   232  					return nil
   233  				}
   234  
   235  				cur = next
   236  			}
   237  			return cur
   238  		},
   239  	})
   240  }
   241  

View as plain text