github.com/sentienttechnologies/studio-go-runner@v0.0.0-20201118202441-6d21f2ced8ee/internal/runner/k8s_test.go (about)

     1  // Copyright 2018-2020 (c) Cognizant Digital Business, Evolutionary AI. All rights reserved. Issued under the Apache 2.0 License.
     2  
     3  package runner
     4  
     5  import (
     6  	"context"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/leaf-ai/studio-go-runner/internal/types"
    11  
    12  	"github.com/karlmutch/k8s"
    13  	core "github.com/karlmutch/k8s/apis/core/v1"
    14  	meta "github.com/karlmutch/k8s/apis/meta/v1"
    15  
    16  	"github.com/go-stack/stack"
    17  	"github.com/jjeffery/kv" // MIT License
    18  
    19  	"github.com/rs/xid"
    20  )
    21  
    22  // This file contains a number of tests that if Kubernetes is detected as the runtime
    23  // the test is being hosted in will be activated and used.  This is a unit test
    24  // that exercises a listener specifically constructed for the purpose of catching
    25  // changes to a configmap.
    26  //
    27  func TestK8sConfigUnit(t *testing.T) {
    28  
    29  	if !*useK8s {
    30  		t.Skip("no Kubernetes cluster present for testing")
    31  	}
    32  
    33  	if err := IsAliveK8s(); err != nil {
    34  		t.Fatal(err)
    35  	}
    36  
    37  	client, errGo := k8s.NewInClusterClient()
    38  	if errGo != nil {
    39  		t.Fatal(kv.Wrap(errGo).With("stack", stack.Trace().TrimRuntime()))
    40  	}
    41  
    42  	name := "test-" + xid.New().String()
    43  
    44  	configMap := &core.ConfigMap{
    45  		Metadata: &meta.ObjectMeta{
    46  			Name:      k8s.String(name),
    47  			Namespace: k8s.String(client.Namespace),
    48  		},
    49  		Data: map[string]string{"STATE": types.K8sRunning.String()},
    50  	}
    51  
    52  	ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute)
    53  	defer cancel()
    54  
    55  	// Establish a listener for the API under test
    56  	updateC := make(chan K8sStateUpdate, 1)
    57  	errC := make(chan kv.Error, 1)
    58  
    59  	go func() {
    60  		// Register a listener for the newly created map
    61  		if err := ListenK8s(ctx, client.Namespace, name, "", updateC, errC); err != nil {
    62  			errC <- err
    63  		}
    64  	}()
    65  
    66  	// Go and create a k8s config map that we can use for testing purposes
    67  	if errGo = client.Create(ctx, configMap); errGo != nil {
    68  		t.Fatal(kv.Wrap(errGo).With("stack", stack.Trace().TrimRuntime()))
    69  	}
    70  
    71  	// Now see if we get the state change with "Running"
    72  	func() {
    73  		for {
    74  			select {
    75  			case <-ctx.Done():
    76  				t.Fatal(kv.NewError("timeout waiting for k8s configmap to change state").With("stack", stack.Trace().TrimRuntime()))
    77  			case state := <-updateC:
    78  				if state.Name == name && state.State == types.K8sRunning {
    79  					return
    80  				}
    81  			}
    82  		}
    83  	}()
    84  
    85  	// Change the map and see if things get notified
    86  	configMap.Data["STATE"] = types.K8sDrainAndSuspend.String()
    87  
    88  	// Go and create a k8s config map that we can use for testing purposes
    89  	if errGo = client.Update(ctx, configMap); errGo != nil {
    90  		t.Fatal(kv.Wrap(errGo).With("stack", stack.Trace().TrimRuntime()))
    91  	}
    92  
    93  	// Now see if we get the state change with "Running"
    94  	func() {
    95  		for {
    96  			select {
    97  			case <-ctx.Done():
    98  				t.Fatal(kv.NewError("timeout waiting for k8s configmap to change state").With("stack", stack.Trace().TrimRuntime()))
    99  			case state := <-updateC:
   100  				if state.Name == name && state.State == types.K8sDrainAndSuspend {
   101  					return
   102  				}
   103  			}
   104  		}
   105  	}()
   106  
   107  	// Cleanup after ourselves
   108  	if errGo = client.Delete(ctx, configMap); errGo != nil {
   109  		t.Fatal(kv.Wrap(errGo).With("stack", stack.Trace().TrimRuntime()))
   110  	}
   111  }