github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/pkg/action/release_testing.go (about)

     1  /*
     2  Copyright The Helm 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 action
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io"
    23  	"time"
    24  
    25  	"github.com/pkg/errors"
    26  	v1 "k8s.io/api/core/v1"
    27  
    28  	"github.com/stefanmcshane/helm/pkg/chartutil"
    29  	"github.com/stefanmcshane/helm/pkg/release"
    30  )
    31  
    32  // ReleaseTesting is the action for testing a release.
    33  //
    34  // It provides the implementation of 'helm test'.
    35  type ReleaseTesting struct {
    36  	cfg     *Configuration
    37  	Timeout time.Duration
    38  	// Used for fetching logs from test pods
    39  	Namespace string
    40  	Filters   map[string][]string
    41  }
    42  
    43  // NewReleaseTesting creates a new ReleaseTesting object with the given configuration.
    44  func NewReleaseTesting(cfg *Configuration) *ReleaseTesting {
    45  	return &ReleaseTesting{
    46  		cfg:     cfg,
    47  		Filters: map[string][]string{},
    48  	}
    49  }
    50  
    51  // Run executes 'helm test' against the given release.
    52  func (r *ReleaseTesting) Run(name string) (*release.Release, error) {
    53  	if err := r.cfg.KubeClient.IsReachable(); err != nil {
    54  		return nil, err
    55  	}
    56  
    57  	if err := chartutil.ValidateReleaseName(name); err != nil {
    58  		return nil, errors.Errorf("releaseTest: Release name is invalid: %s", name)
    59  	}
    60  
    61  	// finds the non-deleted release with the given name
    62  	rel, err := r.cfg.Releases.Last(name)
    63  	if err != nil {
    64  		return rel, err
    65  	}
    66  
    67  	skippedHooks := []*release.Hook{}
    68  	executingHooks := []*release.Hook{}
    69  	if len(r.Filters["!name"]) != 0 {
    70  		for _, h := range rel.Hooks {
    71  			if contains(r.Filters["!name"], h.Name) {
    72  				skippedHooks = append(skippedHooks, h)
    73  			} else {
    74  				executingHooks = append(executingHooks, h)
    75  			}
    76  		}
    77  		rel.Hooks = executingHooks
    78  	}
    79  	if len(r.Filters["name"]) != 0 {
    80  		executingHooks = nil
    81  		for _, h := range rel.Hooks {
    82  			if contains(r.Filters["name"], h.Name) {
    83  				executingHooks = append(executingHooks, h)
    84  			} else {
    85  				skippedHooks = append(skippedHooks, h)
    86  			}
    87  		}
    88  		rel.Hooks = executingHooks
    89  	}
    90  
    91  	if err := r.cfg.execHook(rel, release.HookTest, r.Timeout); err != nil {
    92  		rel.Hooks = append(skippedHooks, rel.Hooks...)
    93  		r.cfg.Releases.Update(rel)
    94  		return rel, err
    95  	}
    96  
    97  	rel.Hooks = append(skippedHooks, rel.Hooks...)
    98  	return rel, r.cfg.Releases.Update(rel)
    99  }
   100  
   101  // GetPodLogs will write the logs for all test pods in the given release into
   102  // the given writer. These can be immediately output to the user or captured for
   103  // other uses
   104  func (r *ReleaseTesting) GetPodLogs(out io.Writer, rel *release.Release) error {
   105  	client, err := r.cfg.KubernetesClientSet()
   106  	if err != nil {
   107  		return errors.Wrap(err, "unable to get kubernetes client to fetch pod logs")
   108  	}
   109  
   110  	for _, h := range rel.Hooks {
   111  		for _, e := range h.Events {
   112  			if e == release.HookTest {
   113  				req := client.CoreV1().Pods(r.Namespace).GetLogs(h.Name, &v1.PodLogOptions{})
   114  				logReader, err := req.Stream(context.Background())
   115  				if err != nil {
   116  					return errors.Wrapf(err, "unable to get pod logs for %s", h.Name)
   117  				}
   118  
   119  				fmt.Fprintf(out, "POD LOGS: %s\n", h.Name)
   120  				_, err = io.Copy(out, logReader)
   121  				fmt.Fprintln(out)
   122  				if err != nil {
   123  					return errors.Wrapf(err, "unable to write pod logs for %s", h.Name)
   124  				}
   125  			}
   126  		}
   127  	}
   128  	return nil
   129  }
   130  
   131  func contains(arr []string, value string) bool {
   132  	for _, item := range arr {
   133  		if item == value {
   134  			return true
   135  		}
   136  	}
   137  	return false
   138  }