@ -12,10 +12,14 @@ import (
"git.netflux.io/rob/kubectl-persistent-logger/logs"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/watch"
dynamicclient "k8s.io/client-go/dynamic/fake"
testclient "k8s.io/client-go/kubernetes/fake"
k8stest "k8s.io/client-go/testing"
)
@ -26,17 +30,28 @@ func (m *mockPodWatcher) WatchPods(ctx context.Context) error { return m.err }
func ( m * mockPodWatcher ) Close ( ) { }
func mockPodwatcherFunc ( err error ) logs . PodWatcherFunc {
return func ( logs . KubernetesClient , string , * metav1 . Label Selector, io . Writer ) logs . PodWatcherInterface {
return func ( logs . KubernetesClient , string , labels . Selector, io . Writer ) logs . PodWatcherInterface {
return & mockPodWatcher { err : err }
}
}
func TestWatcherAllowNonExistent ( t * testing . T ) {
clientset := testclient . NewSimpleClientset ( )
func buildDeployment ( t * testing . T , name string ) * unstructured . Unstructured {
deployment := new ( unstructured . Unstructured )
deployment . SetAPIVersion ( "v1" )
deployment . SetKind ( "deployment" )
deployment . SetName ( "mydeployment" )
deployment . SetNamespace ( "default" )
deployment . SetUID ( types . UID ( "foo" ) )
require . NoError ( t , unstructured . SetNestedField ( deployment . Object , map [ string ] any { "app" : "myapp" } , "spec" , "selector" , "matchLabels" ) )
return deployment
}
func TestWatcherStrictExist ( t * testing . T ) {
client := logs . KubernetesClient { Untyped : dynamicclient . NewSimpleDynamicClient ( runtime . NewScheme ( ) ) }
var buf bytes . Buffer
client := logs . KubernetesClient { Interface : clientset }
watcher := logs . NewWatcher ( "mydeployment" , "mycontainer" , true , client , mockPodwatcherFunc ( nil ) , & buf )
params := logs . WatcherParams { Name : "mydeployment" , Type : "deployments" , Namespace : "default" , StrictExist : true }
watcher := logs . NewWatcher ( params , client , mockPodwatcherFunc ( nil ) , & buf )
err := watcher . Watch ( context . Background ( ) )
assert . EqualError ( t , err , ` deployments.apps "mydeployment" not found ` )
@ -44,19 +59,20 @@ func TestWatcherAllowNonExistent(t *testing.T) {
func TestWatcherPodWatcherError ( t * testing . T ) {
deploymentsWatcher := watch . NewFake ( )
clientset := testclient . NewSimpleClientset (
& appsv1 . Deployment { ObjectMeta : metav1 . ObjectMeta { Name : "mydeployment" , Namespace : "default" } } ,
)
client set. PrependWatchReactor ( "deployments" , k8stest . DefaultWatchReactor ( deploymentsWatcher , nil ) )
untypedClient := dynamicclient . NewSimpleDynamicClient ( runtime . NewScheme ( ) )
untypedClient . PrependWatchReactor ( "deployments" , k8stest . DefaultWatchReactor ( deploymentsWatcher , nil ) )
client := logs . KubernetesClient { Untyped : untypedClient }
var buf bytes . Buffer
client := logs . KubernetesClient { Interface : clientset }
wantErr := errors . New ( "foo" )
watcher := logs . NewWatcher ( "mydeployment" , "mycontainer" , true , client , mockPodwatcherFunc ( wantErr ) , & buf )
params := logs . WatcherParams { Name : "mydeployment" , Type : "deployments" , Namespace : "default" }
watcher := logs . NewWatcher ( params , client , mockPodwatcherFunc ( wantErr ) , & buf )
go func ( ) {
defer deploymentsWatcher . Stop ( )
deployment := & appsv1 . Deployment { ObjectMeta : metav1 . ObjectMeta { Name : "mydeployment" , Namespace : "default" } }
deployment := buildDeployment ( t , "mydeployment" )
deploymentsWatcher . Add ( deployment )
} ( )
@ -66,15 +82,17 @@ func TestWatcherPodWatcherError(t *testing.T) {
func TestWatcherClosedChannel ( t * testing . T ) {
deploymentsWatcher := watch . NewFake ( )
clientset := testclient . NewSimpleClientset (
& appsv1 . Deployment { ObjectMeta : metav1 . ObjectMeta { Name : "mydeployment" , Namespace : "default" } } ,
)
client set. PrependWatchReactor ( "deployments" , k8stest . DefaultWatchReactor ( deploymentsWatcher , nil ) )
untypedClient := dynamicclient . NewSimpleDynamicClient ( runtime . NewScheme ( ) )
untypedClient . PrependWatchReactor ( "deployments" , k8stest . DefaultWatchReactor ( deploymentsWatcher , nil ) )
client := logs . KubernetesClient { Untyped : untypedClient }
var buf bytes . Buffer
client := logs . KubernetesClient { Interface : clientset }
watcher := logs . NewWatcher ( "mydeployment" , "mycontainer" , true , client , nil , & buf )
go deploymentsWatcher . Stop ( )
params := logs . WatcherParams { Name : "mydeployment" , Type : "deployments" , Namespace : "default" }
watcher := logs . NewWatcher ( params , client , nil , & buf )
// Immediately stop the watcher, which closes the ResultChan.
// This should be expected to be handled.
deploymentsWatcher . Stop ( )
ctx , cancel := context . WithTimeout ( context . Background ( ) , time . Millisecond * 500 )
defer cancel ( )
@ -90,31 +108,25 @@ func TestWatcherWithPodWatcher(t *testing.T) {
deploymentsWatcher := watch . NewFake ( )
defer deploymentsWatcher . Stop ( )
podsWatcher := watch . NewFake ( )
defer podsWatcher . Stop ( )
clientset := testclient . NewSimpleClientset ( )
clientset . PrependWatchReactor ( "pods" , k8stest . DefaultWatchReactor ( podsWatcher , nil ) )
clientset . PrependWatchReactor ( "deployments" , k8stest . DefaultWatchReactor ( deploymentsWatcher , nil ) )
typedClient := testclient . NewSimpleClientset ( )
typedClient . PrependWatchReactor ( "pods" , k8stest . DefaultWatchReactor ( podsWatcher , nil ) )
untypedClient := dynamicclient . NewSimpleDynamicClient ( runtime . NewScheme ( ) )
untypedClient . PrependWatchReactor ( "deployments" , k8stest . DefaultWatchReactor ( deploymentsWatcher , nil ) )
client := logs . KubernetesClient { Typed : typedClient , Untyped : untypedClient }
go func ( ) {
deployment := & appsv1 . Deployment { ObjectMeta : metav1 . ObjectMeta { Name : "mydeployment" , Namespace : "default" } }
deployment := buildDeployment ( t , "mydeployment" )
deploymentsWatcher . Add ( deployment )
time . Sleep ( time . Millisecond * 250 )
pods := [ ] * corev1 . Pod {
{
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : "default" } ,
Status : corev1 . PodStatus { Phase : corev1 . PodRunning } ,
} ,
{
ObjectMeta : metav1 . ObjectMeta { Name : "bar" , Namespace : "default" } ,
Status : corev1 . PodStatus { Phase : corev1 . PodRunning } ,
} ,
{
ObjectMeta : metav1 . ObjectMeta { Name : "baz" , Namespace : "default" } ,
Status : corev1 . PodStatus { Phase : corev1 . PodPending } ,
} ,
{ ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : "default" } , Status : corev1 . PodStatus { Phase : corev1 . PodRunning } } ,
{ ObjectMeta : metav1 . ObjectMeta { Name : "bar" , Namespace : "default" } , Status : corev1 . PodStatus { Phase : corev1 . PodRunning } } ,
{ ObjectMeta : metav1 . ObjectMeta { Name : "baz" , Namespace : "default" } , Status : corev1 . PodStatus { Phase : corev1 . PodPending } } ,
}
for _ , pod := range pods {
podsWatcher . Add ( pod )
@ -123,14 +135,14 @@ func TestWatcherWithPodWatcher(t *testing.T) {
} ( )
var buf bytes . Buffer
client := logs . KubernetesClient { Interface : clientset }
watcher := logs . NewWatcher ( "mydeployment" , "mycontainer" , false , client , logs . NewPodWatcher , & buf )
params := logs . WatcherParams { Name : "mydeployment" , Type : "deployments" , Namespace : "default" }
watcher := logs . NewWatcher ( params , client , logs . NewPodWatcher , & buf )
err := watcher . Watch ( ctx )
require . EqualError ( t , err , context . DeadlineExceeded . Error ( ) )
lines := bufToLines ( & buf )
assert . Len ( t , lines , 2 )
assert . ElementsMatch ( t , [ ] string { "[foo] fake logs" , "[bar] fake logs" } , bufToLines ( & buf ) )
require . Len ( t , lines , 2 )
assert . ElementsMatch ( t , [ ] string { "[foo] fake logs" , "[bar] fake logs" } , lines )
}
func bufToLines ( buf * bytes . Buffer ) [ ] string {