1 /* 2 Copyright 2021 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 genericclioptions 18 19 import ( 20 "net/http" 21 "strings" 22 23 "github.com/google/uuid" 24 "github.com/spf13/cobra" 25 ) 26 27 const ( 28 kubectlCommandHeader = "Kubectl-Command" 29 kubectlSessionHeader = "Kubectl-Session" 30 ) 31 32 // CommandHeaderRoundTripper adds a layer around the standard 33 // round tripper to add Request headers before delegation. Implements 34 // the go standard library "http.RoundTripper" interface. 35 type CommandHeaderRoundTripper struct { 36 Delegate http.RoundTripper 37 Headers map[string]string 38 } 39 40 // CommandHeaderRoundTripper adds Request headers before delegating to standard 41 // round tripper. These headers are kubectl command headers which 42 // detail the kubectl command. See SIG CLI KEP 859: 43 // 44 // https://github.com/kubernetes/enhancements/tree/master/keps/sig-cli/859-kubectl-headers 45 func (c *CommandHeaderRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { 46 for header, value := range c.Headers { 47 req.Header.Set(header, value) 48 } 49 return c.Delegate.RoundTrip(req) 50 } 51 52 // ParseCommandHeaders fills in a map of custom headers into the CommandHeaderRoundTripper. These 53 // headers are then filled into each request. For details on the custom headers see: 54 // 55 // https://github.com/kubernetes/enhancements/tree/master/keps/sig-cli/859-kubectl-headers 56 // 57 // Each call overwrites the previously parsed command headers (not additive). 58 // TODO(seans3): Parse/add flags removing PII from flag values. 59 func (c *CommandHeaderRoundTripper) ParseCommandHeaders(cmd *cobra.Command, args []string) { 60 if cmd == nil { 61 return 62 } 63 // Overwrites previously parsed command headers (headers not additive). 64 c.Headers = map[string]string{} 65 // Session identifier to aggregate multiple Requests from single kubectl command. 66 uid := uuid.New().String() 67 c.Headers[kubectlSessionHeader] = uid 68 // Iterate up the hierarchy of commands from the leaf command to create 69 // the full command string. Example: kubectl create secret generic 70 cmdStrs := []string{} 71 for cmd.HasParent() { 72 parent := cmd.Parent() 73 currName := strings.TrimSpace(cmd.Name()) 74 cmdStrs = append([]string{currName}, cmdStrs...) 75 cmd = parent 76 } 77 currName := strings.TrimSpace(cmd.Name()) 78 cmdStrs = append([]string{currName}, cmdStrs...) 79 if len(cmdStrs) > 0 { 80 c.Headers[kubectlCommandHeader] = strings.Join(cmdStrs, " ") 81 } 82 } 83 84 // CancelRequest is propagated to the Delegate RoundTripper within 85 // if the wrapped RoundTripper implements this function. 86 func (c *CommandHeaderRoundTripper) CancelRequest(req *http.Request) { 87 type canceler interface { 88 CancelRequest(*http.Request) 89 } 90 // If possible, call "CancelRequest" on the wrapped Delegate RoundTripper. 91 if cr, ok := c.Delegate.(canceler); ok { 92 cr.CancelRequest(req) 93 } 94 } 95