diff --git a/cmd/sonobuoy/app/args.go b/cmd/sonobuoy/app/args.go index 47b696b83..e1a168294 100644 --- a/cmd/sonobuoy/app/args.go +++ b/cmd/sonobuoy/app/args.go @@ -120,3 +120,11 @@ func AddRBACModeFlags(mode *RBACMode, cmd *cobra.Command, defaultMode RBACMode) `Whether to enable rbac on Sonobuoy. Options are "enable", "disable", and "detect" (query the server to see whether to enable Sonobuoy)`, ) } + +// AddSkipPreflightFlag adds a boolean flag to skip preflight checks. +func AddSkipPreflightFlag(flag *bool, cmd *cobra.Command) { + cmd.PersistentFlags().BoolVar( + flag, "preflight", false, + "If true, skip all checks before kicking off the sonobuoy run.", + ) +} diff --git a/cmd/sonobuoy/app/run.go b/cmd/sonobuoy/app/run.go index 32a8b9095..67182a68e 100644 --- a/cmd/sonobuoy/app/run.go +++ b/cmd/sonobuoy/app/run.go @@ -51,6 +51,7 @@ func init() { AddKubeconfigFlag(&runFlags.kubecfg, cmd) // Default to detect since we need a kubeconfig regardless AddRBACModeFlags(&runFlags.rbacMode, cmd, DetectRBACMode) + AddSkipPreflightFlag(&runopts.SkipPreflight, cmd) RootCmd.AddCommand(cmd) } diff --git a/pkg/client/interfaces.go b/pkg/client/interfaces.go index fffeb78ec..e48b9e6e5 100644 --- a/pkg/client/interfaces.go +++ b/pkg/client/interfaces.go @@ -51,6 +51,8 @@ type E2EConfig struct { // RunConfig are the input options for running Sonobuoy. type RunConfig struct { GenConfig + // SkipPreflight means don't run any checks before kicking off the Sonobuoy run. + SkipPreflight bool } // RetrieveConfig are the options passed to RetrieveResults. diff --git a/pkg/client/run.go b/pkg/client/run.go index 1663c1c59..2929f9915 100644 --- a/pkg/client/run.go +++ b/pkg/client/run.go @@ -23,13 +23,14 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/client-go/discovery" "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" ) @@ -37,6 +38,16 @@ import ( const bufferSize = 4096 func (c *SonobuoyClient) Run(cfg *RunConfig, restConfig *rest.Config) error { + if !cfg.SkipPreflight { + client, err := kubernetes.NewForConfig(restConfig) + if err != nil { + return errors.Wrap(err, "couln't create Kubernetes client") + } + if err := preflightCheck(client); err != nil { + return errors.Wrap(err, "preflight check failed") + } + } + manifest, err := c.GenerateManifest(&cfg.GenConfig) if err != nil { return errors.Wrap(err, "couldn't run invalid manifest") @@ -99,7 +110,7 @@ func createObject(cfg *rest.Config, obj *unstructured.Unstructured, mapper meta. return errors.Wrap(err, "couldn't retrive object metadata") } - _, err = client.Resource(&v1.APIResource{ + _, err = client.Resource(&metav1.APIResource{ Name: resource, Namespaced: namespace != "", }, namespace).Create(obj) @@ -167,3 +178,26 @@ func unstructuredVersionInterface(version schema.GroupVersion) (*meta.VersionInt MetadataAccessor: meta.NewAccessor(), }, nil } + +const ( + kubeSystemNamespace = "kube-system" + kubeDNSLabelKey = "k8s-app" + kubeDNSLabelValue = "kube-dns" +) + +func preflightCheck(client kubernetes.Interface) error { + selector := metav1.AddLabelToSelector(&metav1.LabelSelector{}, kubeDNSLabelKey, kubeDNSLabelValue) + + obj, err := client.CoreV1().Pods(kubeSystemNamespace).List( + metav1.ListOptions{LabelSelector: selector.String()}, + ) + if err != nil { + return errors.Wrap(err, "could not retrieve list of pods") + } + + if len(obj.Items) == 0 { + return errors.New("no kube-dns tests found") + } + + return nil +}