Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR 2: Feature/logrus implementation #3

Merged
merged 3 commits into from
Nov 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 5 additions & 23 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,9 @@ package cmd

import (
"fmt"
"os"
"github.com/spf13/cobra"

homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/viper"

"os"
)


Expand Down Expand Up @@ -57,17 +54,8 @@ func Execute() {

func init() {
cobra.OnInitialize(initConfig)

// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.

rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.anchorctl.yaml)")


// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is current dir)")
rootCmd.PersistentFlags().IntP( "verbose", "v", 3, "Verbose level, Accepts 1-7")
}


Expand All @@ -77,15 +65,10 @@ func initConfig() {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
// Find home directory.
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
os.Exit(1)
}

// Search config in home directory with name ".anchorctl" (without extension).
viper.AddConfigPath(home)
viper.AddConfigPath(".")
viper.AddConfigPath("/config/")
viper.SetConfigName(".anchorctl")
}

Expand All @@ -96,4 +79,3 @@ func initConfig() {
fmt.Println("Using config file:", viper.ConfigFileUsed())
}
}

109 changes: 63 additions & 46 deletions cmd/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,67 +16,84 @@ limitations under the License.
package cmd

import (
"fmt"

"os"
"github.com/sirupsen/logrus"
"github.com/anchorageio/anchorctl/utils/kubernetes"
"github.com/anchorageio/anchorctl/utils/logging"
"github.com/spf13/cobra"
)

var description = `
Test takes in the test steps as config and executes the tests in order.

Example usage: anchorctl test --file test.yaml --kind kubetest

Kinds of tests include:
- kubetest: Runs tests against the current context of a kube cluster. Requires --kubeconfig flag to be set.
Types of tests include:
- AssetJSONPath: Given jsonPath and value, assert that the jsonpath of object in cluster matches the value.
- AssertValidation: Given action, filepath and expected error, apply the action to the object in file, and assert error is returned
- AssertMutation: Given action, filepath, jsonPath and value, assert jsonpath after applying the object in the file.
`

// testCmd represents the test command
var testCmd = &cobra.Command{
Use: "test",
Short: "Command to run Anchor tests",
Long: `Test takes in the test steps as config and executes the tests in order.
Long: description,
Run: testExecute,
}

Example usage: anchorctl test -f test.yaml
func init() {
rootCmd.AddCommand(testCmd)

Kinds of tests include:
- kubetest: Requires --kubeconfig flag to be set. Runs tests against the current context of a kube cluster.
Types of tests include:
- AssetJSONPath: Example fields are type: AssertJSONPath, jsonPath: ".spec.nodeName", value: "docker-desktop"`,
RunE: func(cmd *cobra.Command, args []string) error {
// Local Flags
testCmd.Flags().StringP("file", "f", "", "Input file with the tests.")
testCmd.Flags().StringP("kubeconfig", "c", "~/.kube/config", "Path to kubeconfig file.")
testCmd.Flags().StringP("kind", "k", "kubetest", "Kind of test, only kubetest is supported at the moment.")
testCmd.Flags().Float64P("threshold", "t", 80, "Percentage of tests to pass, else return failure.")
testCmd.Flags().IntP("verbose", "v", 3, "Verbosity Level, choose between 1 being Fatal - 7 being .")
}

kind, err := cmd.Flags().GetString("kind")
func testExecute(cmd *cobra.Command, args []string) {
verbosity, err := cmd.Flags().GetInt("verbose")
if err != nil {
logrus.WithFields(logrus.Fields{ "flag": "verbose"}).Error("Unable to parse flag. Defaulting to INFO.")
verbosity = 5
}
log := &logging.Logger{}
log.SetVerbosity(verbosity)
logger := log.GetLogger()

kind, err := cmd.Flags().GetString("kind")
if err != nil {
logger.WithFields(logrus.Fields{ "flag": "kind"}).Error("Unable to parse flag. Defaulting to kubetest.")
kind = "kubetest"
}

testfile, err := cmd.Flags().GetString("file")
if err != nil {
logger.WithFields(logrus.Fields{ "flag": "file"}).Fatal("Unable to parse flag.")
}

threshold, err := cmd.Flags().GetFloat64("threshold")
if err != nil {
logger.WithFields(logrus.Fields{ "flag": "threshold"}).Error("Unable to parse flag. Defaulting to 100.")
}

switch kind {

case "kubetest":
kubeconfig, err := cmd.Flags().GetString("kubeconfig")
if err != nil {
return fmt.Errorf("Could not parse kind flag", err.Error())
logger.WithFields(logrus.Fields{ "flag": "kubeconfig"}).Fatal("Unable to parse flag.")
}

testfile, err := cmd.Flags().GetString("file")
logger.WithFields(logrus.Fields{ "kind": "kubetest"}).Info("Starting kube assert")
err = kubernetes.Assert(log, threshold, kubeconfig, testfile)
if err != nil {
return fmt.Errorf("Could not parse testfile flag", err.Error())
}

switch kind {

case "kubetest":
kubeconfig, err := cmd.Flags().GetString("kubeconfig")
if err != nil {
return fmt.Errorf("Could not parse kubeconfig flag", err.Error())
}

err = kubernetes.Assert(cmd, kubeconfig, testfile)
if err != nil {
return fmt.Errorf("KubeTest errored out", err.Error())
}

os.Exit(1)
}

return nil
},
}

func init() {
rootCmd.AddCommand(testCmd)

// Here you will define your flags and configuration settings.

// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// testCmd.PersistentFlags().String("foo", "", "A help for foo")

// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
testCmd.Flags().StringP("file", "f", "", "Input file with the tests.")
testCmd.Flags().StringP("kubeconfig", "c", "~/.kube/config", "Path to kubeconfig file")
testCmd.Flags().StringP("kind", "k", "~/.kube/config", "Path to kubeconfig file")
}
}
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ go 1.13

require (
github.com/imdario/mergo v0.3.8 // indirect
github.com/mitchellh/go-homedir v1.1.0
github.com/sirupsen/logrus v1.2.0
github.com/spf13/cobra v0.0.5
github.com/spf13/viper v1.5.0
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0
gopkg.in/yaml.v2 v2.2.4
k8s.io/api v0.0.0-20191112020540-7f9008e52f64
k8s.io/apimachinery v0.0.0-20191111054156-6eb29fdf75dc
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
Expand Down Expand Up @@ -157,6 +158,7 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
Expand Down Expand Up @@ -190,8 +192,6 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
Expand Down
21 changes: 21 additions & 0 deletions samples/fixtures/deploy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
run: nginx-labels
name: nginx-labels
namespace: applications
spec:
replicas: 1
selector:
matchLabels:
run: nginx-labels
template:
metadata:
labels:
run: nginx-labels
spec:
containers:
- image: nginx
name: nginx-labels
resources: {}
18 changes: 18 additions & 0 deletions samples/fixtures/loadbalancer.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: v1
kind: Service
metadata:
name: hello-world
namespace: applications
labels:
app: hello-world
spec:
ports:
- name: 8765-8765
port: 8765
protocol: TCP
targetPort: 8765
selector:
app: hello-world
type: LoadBalancer
status:
loadBalancer: {}
15 changes: 12 additions & 3 deletions samples/kube-test.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
object:
kind: Pod
metadata:
# name: nginx-7db9fccd9b-jlpw4
namespace: default
kind: pod
label:
key: run
value: nginx
file: ./something.yaml

tests:
- type: AssertJSONPath
jsonPath: ".spec.nodeName"
value: "docker-desktop"
- type: AssertValidation
action: "CREATE" # Allowed actions: CREATE, UPDATE, DELETE
# filePath: "/Users/cherukat/go/src/github.com/anchorageio/anchorctl/samples/fixtures/loadbalancer.yaml"
filePath: "./samples/fixtures/loadbalancer.yaml"
expectedError: "Internal error occurred: admission webhook \"webhook.openpolicyagent.org\" denied the request: External Loadbalancers cannot be deployed in this cluster"
- type: AssertMutation
action: "CREATE" # Allowed actions: CREATE, UPDATE
filePath: "./samples/fixtures/deploy.yaml"
jsonPath: ".metadata.labels.function"
value: "workload"
56 changes: 44 additions & 12 deletions utils/kubernetes/assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,69 @@ package kubernetes

import (
"bytes"
"github.com/spf13/cobra"
"fmt"
"github.com/anchorageio/anchorctl/utils/logging"
log "github.com/sirupsen/logrus"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/util/jsonpath"
)

func assertJsonpath(cmd *cobra.Command, object interface{}, path, value string) (bool, error) {
func assertJsonpath(logging *logging.Logger, object interface{}, path, value string) (bool, error) {

jp := jsonpath.New("assertJsonpath")
jp.AllowMissingKeys(true)
err := jp.Parse("{" + path + "}")
passed := true
logger := logging.Log

if err != nil {
cmd.PrintErrln("Cannot parse jsonpath. ", err)
logger.WithFields(log.Fields{ "jsonPath": path,}).Error("Cannot parse JSONPath")
return false, err
}

buf := new(bytes.Buffer)

err = jp.Execute(buf, object)
objects := getSlice(object)

if err != nil {
cmd.PrintErrln("Error executing jsonpath on object. ", err)
return false, err
for _, i := range objects {

err = jp.Execute(buf, i)
if err != nil || buf.String() != value {
logger.WithFields(log.Fields{ "jsonPath": path,}).Error("Cannot execute JSONPath")
passed = false
break
}
buf.Reset()

}

return passed, err
}

func assertValidation(client *kubernetes.Clientset, action, filepath, expectedError string) bool {
_, _, err := applyAction(client, filepath, action)

if err != nil && err.Error() == expectedError {
return true
}

if buf.String() == value {
cmd.Println("PASSED: " + path + " " + value)
return true, nil
return false

}

func assertMutation(client *kubernetes.Clientset, logging *logging.Logger, action, filepath, jsonpath, value string) (bool, error) {
_, obj, err := applyAction(client, filepath, action)
if err != nil {
return false, fmt.Errorf("Errored out: ", err)
}

cmd.Println("FAILED: expected" + value + " got " + buf.String())
return assertJsonpath(logging, obj, jsonpath, value)

return false, nil
}

func assertNetworkPolicies(sourceNamespace, sourceLabelKey, sourceLabelValue, destNamespace, destNamespaceKey, destValue, port, ipaddress string){
// TODO: If ip address is provided, check it returns a 200 with the port
// TODO: Else, create pod in sourceNamespace with sourceLabelKey and sourceLabelValue
// TODO: Create pod in destNamespace with destLabelKey and destLabelValue
// TODO: Exec into source pod, telnet destination pod in the provided IP address
}
Loading