...

Source file src/k8s.io/client-go/tools/remotecommand/v4.go

Documentation: k8s.io/client-go/tools/remotecommand

     1  /*
     2  Copyright 2016 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 remotecommand
    18  
    19  import (
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  	"strconv"
    24  	"sync"
    25  
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/util/remotecommand"
    28  	"k8s.io/client-go/util/exec"
    29  )
    30  
    31  // streamProtocolV4 implements version 4 of the streaming protocol for attach
    32  // and exec. This version adds support for exit codes on the error stream through
    33  // the use of metav1.Status instead of plain text messages.
    34  type streamProtocolV4 struct {
    35  	*streamProtocolV3
    36  }
    37  
    38  var _ streamProtocolHandler = &streamProtocolV4{}
    39  
    40  func newStreamProtocolV4(options StreamOptions) streamProtocolHandler {
    41  	return &streamProtocolV4{
    42  		streamProtocolV3: newStreamProtocolV3(options).(*streamProtocolV3),
    43  	}
    44  }
    45  
    46  func (p *streamProtocolV4) createStreams(conn streamCreator) error {
    47  	return p.streamProtocolV3.createStreams(conn)
    48  }
    49  
    50  func (p *streamProtocolV4) handleResizes() {
    51  	p.streamProtocolV3.handleResizes()
    52  }
    53  
    54  func (p *streamProtocolV4) stream(conn streamCreator) error {
    55  	if err := p.createStreams(conn); err != nil {
    56  		return err
    57  	}
    58  
    59  	// now that all the streams have been created, proceed with reading & copying
    60  
    61  	errorChan := watchErrorStream(p.errorStream, &errorDecoderV4{})
    62  
    63  	p.handleResizes()
    64  
    65  	p.copyStdin()
    66  
    67  	var wg sync.WaitGroup
    68  	p.copyStdout(&wg)
    69  	p.copyStderr(&wg)
    70  
    71  	// we're waiting for stdout/stderr to finish copying
    72  	wg.Wait()
    73  
    74  	// waits for errorStream to finish reading with an error or nil
    75  	return <-errorChan
    76  }
    77  
    78  // errorDecoderV4 interprets the json-marshaled metav1.Status on the error channel
    79  // and creates an exec.ExitError from it.
    80  type errorDecoderV4 struct{}
    81  
    82  func (d *errorDecoderV4) decode(message []byte) error {
    83  	status := metav1.Status{}
    84  	err := json.Unmarshal(message, &status)
    85  	if err != nil {
    86  		return fmt.Errorf("error stream protocol error: %v in %q", err, string(message))
    87  	}
    88  	switch status.Status {
    89  	case metav1.StatusSuccess:
    90  		return nil
    91  	case metav1.StatusFailure:
    92  		if status.Reason == remotecommand.NonZeroExitCodeReason {
    93  			if status.Details == nil {
    94  				return errors.New("error stream protocol error: details must be set")
    95  			}
    96  			for i := range status.Details.Causes {
    97  				c := &status.Details.Causes[i]
    98  				if c.Type != remotecommand.ExitCodeCauseType {
    99  					continue
   100  				}
   101  
   102  				rc, err := strconv.ParseUint(c.Message, 10, 8)
   103  				if err != nil {
   104  					return fmt.Errorf("error stream protocol error: invalid exit code value %q", c.Message)
   105  				}
   106  				return exec.CodeExitError{
   107  					Err:  fmt.Errorf("command terminated with exit code %d", rc),
   108  					Code: int(rc),
   109  				}
   110  			}
   111  
   112  			return fmt.Errorf("error stream protocol error: no %s cause given", remotecommand.ExitCodeCauseType)
   113  		}
   114  	default:
   115  		return errors.New("error stream protocol error: unknown error")
   116  	}
   117  
   118  	return fmt.Errorf(status.Message)
   119  }
   120  

View as plain text