2017-08-11 16:59:57 +00:00
// Copyright © 2017 Aqua Security Software Ltd. <info@aquasec.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
2017-08-30 17:01:53 +00:00
"os"
"reflect"
2017-08-11 16:59:57 +00:00
"regexp"
2017-08-15 15:44:40 +00:00
"strconv"
2017-08-11 16:59:57 +00:00
"testing"
2017-08-30 17:01:53 +00:00
"github.com/spf13/viper"
2017-08-11 16:59:57 +00:00
)
func TestCheckVersion ( t * testing . T ) {
kubeoutput := ` Client Version : version . Info { Major : "1" , Minor : "7" , GitVersion : "v1.7.0" , GitCommit : "d3ada0119e776222f11ec7945e6d860061339aad" , GitTreeState : "clean" , BuildDate : "2017-06-30T09:51:01Z" , GoVersion : "go1.8.3" , Compiler : "gc" , Platform : "darwin/amd64" }
Server Version : version . Info { Major : "1" , Minor : "7" , GitVersion : "v1.7.0" , GitCommit : "d3ada0119e776222f11ec7945e6d860061339aad" , GitTreeState : "clean" , BuildDate : "2017-07-26T00:12:31Z" , GoVersion : "go1.8.3" , Compiler : "gc" , Platform : "linux/amd64" } `
cases := [ ] struct {
t string
s string
major string
minor string
exp string
} {
{ t : "Client" , s : kubeoutput , major : "1" , minor : "7" } ,
{ t : "Server" , s : kubeoutput , major : "1" , minor : "7" } ,
{ t : "Client" , s : kubeoutput , major : "1" , minor : "6" , exp : "Unexpected Client version 1.7" } ,
{ t : "Client" , s : kubeoutput , major : "2" , minor : "0" , exp : "Unexpected Client version 1.7" } ,
{ t : "Server" , s : "something unexpected" , major : "2" , minor : "0" , exp : "Couldn't find Server version from kubectl output 'something unexpected'" } ,
}
2017-08-15 15:44:40 +00:00
for id , c := range cases {
t . Run ( strconv . Itoa ( id ) , func ( t * testing . T ) {
2017-08-11 16:59:57 +00:00
m := checkVersion ( c . t , c . s , c . major , c . minor )
if m != c . exp {
t . Fatalf ( "Got: %s, expected: %s" , m , c . exp )
}
} )
}
}
func TestVersionMatch ( t * testing . T ) {
minor := regexVersionMinor
major := regexVersionMajor
client := ` Client Version: version.Info { Major:"1", Minor:"7", GitVersion:"v1.7.0", GitCommit:"d3ada0119e776222f11ec7945e6d860061339aad", GitTreeState:"clean", BuildDate:"2017-06-30T09:51:01Z", GoVersion:"go1.8.3", Compiler:"gc", Platform:"darwin/amd64"} `
server := ` Server Version: version.Info { Major:"1", Minor:"7", GitVersion:"v1.7.0", GitCommit:"d3ada0119e776222f11ec7945e6d860061339aad", GitTreeState:"clean", BuildDate:"2017-07-26T00:12:31Z", GoVersion:"go1.8.3", Compiler:"gc", Platform:"linux/amd64"} `
cases := [ ] struct {
r * regexp . Regexp
s string
exp string
} {
{ r : major , s : server , exp : "1" } ,
{ r : minor , s : server , exp : "7" } ,
{ r : major , s : client , exp : "1" } ,
{ r : minor , s : client , exp : "7" } ,
{ r : major , s : "Some unexpected string" } ,
{ r : minor } , // Checking that we don't fall over if the string is empty
}
2017-08-15 15:44:40 +00:00
for id , c := range cases {
t . Run ( strconv . Itoa ( id ) , func ( t * testing . T ) {
2017-08-11 16:59:57 +00:00
m := versionMatch ( c . r , c . s )
if m != c . exp {
t . Fatalf ( "Got %s expected %s" , m , c . exp )
}
} )
}
}
2017-08-15 15:44:40 +00:00
var g string
2017-08-30 17:01:53 +00:00
var e [ ] error
var eIndex int
2017-08-15 15:44:40 +00:00
func fakeps ( proc string ) string {
return g
}
2017-08-30 17:01:53 +00:00
func fakestat ( file string ) ( os . FileInfo , error ) {
err := e [ eIndex ]
eIndex ++
return nil , err
}
2017-08-15 15:44:40 +00:00
func TestVerifyBin ( t * testing . T ) {
cases := [ ] struct {
proc string
psOut string
exp bool
} {
{ proc : "single" , psOut : "single" , exp : true } ,
{ proc : "single" , psOut : "" , exp : false } ,
{ proc : "two words" , psOut : "two words" , exp : true } ,
{ proc : "two words" , psOut : "" , exp : false } ,
{ proc : "cmd" , psOut : "cmd param1 param2" , exp : true } ,
{ proc : "cmd param" , psOut : "cmd param1 param2" , exp : true } ,
{ proc : "cmd param" , psOut : "cmd" , exp : false } ,
2017-08-31 15:01:31 +00:00
{ proc : "cmd" , psOut : "cmd x \ncmd y" , exp : true } ,
{ proc : "cmd y" , psOut : "cmd x \ncmd y" , exp : true } ,
{ proc : "cmd" , psOut : "/usr/bin/cmd" , exp : true } ,
{ proc : "cmd" , psOut : "kube-cmd" , exp : false } ,
{ proc : "cmd" , psOut : "/usr/bin/kube-cmd" , exp : false } ,
2017-08-15 15:44:40 +00:00
}
2017-08-30 16:51:28 +00:00
psFunc = fakeps
2017-08-15 15:44:40 +00:00
for id , c := range cases {
t . Run ( strconv . Itoa ( id ) , func ( t * testing . T ) {
g = c . psOut
2017-08-30 16:51:28 +00:00
v := verifyBin ( c . proc )
2017-08-15 15:44:40 +00:00
if v != c . exp {
t . Fatalf ( "Expected %v got %v" , c . exp , v )
}
} )
}
}
2017-08-15 16:00:35 +00:00
2017-08-30 11:07:46 +00:00
func TestFindExecutable ( t * testing . T ) {
cases := [ ] struct {
candidates [ ] string // list of executables we'd consider
psOut string // fake output from ps
exp string // the one we expect to find in the (fake) ps output
expErr bool
} {
{ candidates : [ ] string { "one" , "two" , "three" } , psOut : "two" , exp : "two" } ,
{ candidates : [ ] string { "one" , "two" , "three" } , psOut : "two three" , exp : "two" } ,
{ candidates : [ ] string { "one double" , "two double" , "three double" } , psOut : "two double is running" , exp : "two double" } ,
{ candidates : [ ] string { "one" , "two" , "three" } , psOut : "blah" , expErr : true } ,
{ candidates : [ ] string { "one double" , "two double" , "three double" } , psOut : "two" , expErr : true } ,
2017-08-30 16:51:28 +00:00
{ candidates : [ ] string { "apiserver" , "kube-apiserver" } , psOut : "kube-apiserver" , exp : "kube-apiserver" } ,
{ candidates : [ ] string { "apiserver" , "kube-apiserver" , "hyperkube-apiserver" } , psOut : "kube-apiserver" , exp : "kube-apiserver" } ,
2017-08-30 11:07:46 +00:00
}
2017-08-30 16:51:28 +00:00
psFunc = fakeps
2017-08-30 11:07:46 +00:00
for id , c := range cases {
t . Run ( strconv . Itoa ( id ) , func ( t * testing . T ) {
g = c . psOut
2017-08-30 16:51:28 +00:00
e , err := findExecutable ( c . candidates )
2017-08-30 11:07:46 +00:00
if e != c . exp {
t . Fatalf ( "Expected %v got %v" , c . exp , e )
}
if err == nil && c . expErr {
t . Fatalf ( "Expected error" )
}
if err != nil && ! c . expErr {
t . Fatalf ( "Didn't expect error: %v" , err )
}
} )
}
}
2017-08-30 17:01:53 +00:00
func TestGetBinaries ( t * testing . T ) {
cases := [ ] struct {
config map [ string ] interface { }
psOut string
exp map [ string ] string
} {
{
2017-08-31 14:22:30 +00:00
config : map [ string ] interface { } { "components" : [ ] string { "apiserver" } , "apiserver" : map [ string ] interface { } { "bins" : [ ] string { "apiserver" , "kube-apiserver" } } } ,
2017-08-30 17:01:53 +00:00
psOut : "kube-apiserver" ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" } ,
} ,
{
2017-08-31 14:22:30 +00:00
// "thing" is not in the list of components
config : map [ string ] interface { } { "components" : [ ] string { "apiserver" } , "apiserver" : map [ string ] interface { } { "bins" : [ ] string { "apiserver" , "kube-apiserver" } } , "thing" : map [ string ] interface { } { "bins" : [ ] string { "something else" , "thing" } } } ,
psOut : "kube-apiserver thing" ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" } ,
} ,
{
// "anotherthing" in list of components but doesn't have a defintion
config : map [ string ] interface { } { "components" : [ ] string { "apiserver" , "anotherthing" } , "apiserver" : map [ string ] interface { } { "bins" : [ ] string { "apiserver" , "kube-apiserver" } } , "thing" : map [ string ] interface { } { "bins" : [ ] string { "something else" , "thing" } } } ,
psOut : "kube-apiserver thing" ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" } ,
} ,
{
// more than one component
config : map [ string ] interface { } { "components" : [ ] string { "apiserver" , "thing" } , "apiserver" : map [ string ] interface { } { "bins" : [ ] string { "apiserver" , "kube-apiserver" } } , "thing" : map [ string ] interface { } { "bins" : [ ] string { "something else" , "thing" } } } ,
2017-08-31 16:43:07 +00:00
psOut : "kube-apiserver \nthing" ,
2017-08-30 17:01:53 +00:00
exp : map [ string ] string { "apiserver" : "kube-apiserver" , "thing" : "thing" } ,
} ,
2017-08-31 14:22:30 +00:00
{
// default binary to component name
config : map [ string ] interface { } { "components" : [ ] string { "apiserver" , "thing" } , "apiserver" : map [ string ] interface { } { "bins" : [ ] string { "apiserver" , "kube-apiserver" } } , "thing" : map [ string ] interface { } { "bins" : [ ] string { "something else" , "thing" } , "optional" : true } } ,
2017-08-31 16:43:07 +00:00
psOut : "kube-apiserver \notherthing some params" ,
2017-08-31 14:22:30 +00:00
exp : map [ string ] string { "apiserver" : "kube-apiserver" , "thing" : "thing" } ,
} ,
2017-08-30 17:01:53 +00:00
}
v := viper . New ( )
psFunc = fakeps
for id , c := range cases {
t . Run ( strconv . Itoa ( id ) , func ( t * testing . T ) {
g = c . psOut
for k , val := range c . config {
v . Set ( k , val )
}
2017-08-31 14:22:30 +00:00
m := getBinaries ( v )
2017-08-30 17:01:53 +00:00
if ! reflect . DeepEqual ( m , c . exp ) {
t . Fatalf ( "Got %v\nExpected %v" , m , c . exp )
}
} )
}
}
2017-08-15 16:00:35 +00:00
func TestMultiWordReplace ( t * testing . T ) {
cases := [ ] struct {
input string
sub string
subname string
output string
} {
{ input : "Here's a file with no substitutions" , sub : "blah" , subname : "blah" , output : "Here's a file with no substitutions" } ,
{ input : "Here's a file with a substitution" , sub : "blah" , subname : "substitution" , output : "Here's a file with a blah" } ,
{ input : "Here's a file with multi-word substitutions" , sub : "multi word" , subname : "multi-word" , output : "Here's a file with 'multi word' substitutions" } ,
{ input : "Here's a file with several several substitutions several" , sub : "blah" , subname : "several" , output : "Here's a file with blah blah substitutions blah" } ,
}
for id , c := range cases {
t . Run ( strconv . Itoa ( id ) , func ( t * testing . T ) {
s := multiWordReplace ( c . input , c . subname , c . sub )
if s != c . output {
t . Fatalf ( "Expected %s got %s" , c . output , s )
}
} )
}
}
2017-08-30 17:01:53 +00:00
func TestFindConfigFile ( t * testing . T ) {
cases := [ ] struct {
input [ ] string
statResults [ ] error
exp string
} {
{ input : [ ] string { "myfile" } , statResults : [ ] error { nil } , exp : "myfile" } ,
{ input : [ ] string { "thisfile" , "thatfile" } , statResults : [ ] error { os . ErrNotExist , nil } , exp : "thatfile" } ,
{ input : [ ] string { "thisfile" , "thatfile" } , statResults : [ ] error { os . ErrNotExist , os . ErrNotExist } , exp : "" } ,
}
statFunc = fakestat
for id , c := range cases {
t . Run ( strconv . Itoa ( id ) , func ( t * testing . T ) {
e = c . statResults
eIndex = 0
conf := findConfigFile ( c . input )
if conf != c . exp {
t . Fatalf ( "Got %s expected %s" , conf , c . exp )
}
} )
}
}
func TestGetConfigFiles ( t * testing . T ) {
cases := [ ] struct {
config map [ string ] interface { }
exp map [ string ] string
statResults [ ] error
} {
{
2017-08-31 14:22:30 +00:00
config : map [ string ] interface { } { "components" : [ ] string { "apiserver" } , "apiserver" : map [ string ] interface { } { "confs" : [ ] string { "apiserver" , "kube-apiserver" } } } ,
statResults : [ ] error { os . ErrNotExist , nil } ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" } ,
} ,
{
// Component "thing" isn't included in the list of components
config : map [ string ] interface { } {
"components" : [ ] string { "apiserver" } ,
"apiserver" : map [ string ] interface { } { "confs" : [ ] string { "apiserver" , "kube-apiserver" } } ,
"thing" : map [ string ] interface { } { "confs" : [ ] string { "/my/file/thing" } } } ,
2017-08-30 17:01:53 +00:00
statResults : [ ] error { os . ErrNotExist , nil } ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" } ,
} ,
{
2017-08-31 14:22:30 +00:00
// More than one component
config : map [ string ] interface { } {
"components" : [ ] string { "apiserver" , "thing" } ,
"apiserver" : map [ string ] interface { } { "confs" : [ ] string { "apiserver" , "kube-apiserver" } } ,
"thing" : map [ string ] interface { } { "confs" : [ ] string { "/my/file/thing" } } } ,
2017-08-30 17:01:53 +00:00
statResults : [ ] error { os . ErrNotExist , nil , nil } ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" , "thing" : "/my/file/thing" } ,
} ,
2017-08-31 14:22:30 +00:00
{
// Default thing to specified default config
config : map [ string ] interface { } {
"components" : [ ] string { "apiserver" , "thing" } ,
"apiserver" : map [ string ] interface { } { "confs" : [ ] string { "apiserver" , "kube-apiserver" } } ,
"thing" : map [ string ] interface { } { "confs" : [ ] string { "/my/file/thing" } , "defaultconf" : "another/thing" } } ,
statResults : [ ] error { os . ErrNotExist , nil , os . ErrNotExist } ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" , "thing" : "another/thing" } ,
} ,
{
// Default thing to component name
config : map [ string ] interface { } {
"components" : [ ] string { "apiserver" , "thing" } ,
"apiserver" : map [ string ] interface { } { "confs" : [ ] string { "apiserver" , "kube-apiserver" } } ,
"thing" : map [ string ] interface { } { "confs" : [ ] string { "/my/file/thing" } } } ,
statResults : [ ] error { os . ErrNotExist , nil , os . ErrNotExist } ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" , "thing" : "thing" } ,
} ,
2017-08-30 17:01:53 +00:00
}
v := viper . New ( )
statFunc = fakestat
for id , c := range cases {
t . Run ( strconv . Itoa ( id ) , func ( t * testing . T ) {
for k , val := range c . config {
v . Set ( k , val )
}
e = c . statResults
eIndex = 0
m := getConfigFiles ( v )
if ! reflect . DeepEqual ( m , c . exp ) {
t . Fatalf ( "Got %v\nExpected %v" , m , c . exp )
}
} )
}
}
2017-08-30 17:37:01 +00:00
func TestMakeSubsitutions ( t * testing . T ) {
cases := [ ] struct {
input string
subst map [ string ] string
exp string
} {
{ input : "Replace $thisbin" , subst : map [ string ] string { "this" : "that" } , exp : "Replace that" } ,
{ input : "Replace $thisbin" , subst : map [ string ] string { "this" : "that" , "here" : "there" } , exp : "Replace that" } ,
{ input : "Replace $thisbin and $herebin" , subst : map [ string ] string { "this" : "that" , "here" : "there" } , exp : "Replace that and there" } ,
}
for _ , c := range cases {
t . Run ( c . input , func ( t * testing . T ) {
s := makeSubstitutions ( c . input , "bin" , c . subst )
if s != c . exp {
t . Fatalf ( "Got %s expected %s" , s , c . exp )
}
} )
}
}