Merge pull request #45 from digitalocean/varsha/context-timeout
Allow users to configure timeout while building kubernetes clientimage-warning-sha256 v0.1.0
commit
80b6b4a8b1
|
@ -17,8 +17,6 @@ limitations under the License.
|
|||
package basic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/digitalocean/clusterlint/checks"
|
||||
"github.com/digitalocean/clusterlint/kube"
|
||||
)
|
||||
|
@ -50,7 +48,6 @@ func (b *barePodCheck) Run(objects *kube.Objects) ([]checks.Diagnostic, error) {
|
|||
var diagnostics []checks.Diagnostic
|
||||
for _, pod := range objects.Pods.Items {
|
||||
pod := pod
|
||||
fmt.Println(pod.ObjectMeta.OwnerReferences)
|
||||
if len(pod.ObjectMeta.OwnerReferences) == 0 {
|
||||
d := checks.Diagnostic{
|
||||
Check: b.Name(),
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package checks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
|
@ -25,8 +26,8 @@ import (
|
|||
)
|
||||
|
||||
// Run applies the filters and runs the resultant check list in parallel
|
||||
func Run(client *kube.Client, checkFilter CheckFilter, diagnosticFilter DiagnosticFilter) ([]Diagnostic, error) {
|
||||
objects, err := client.FetchObjects()
|
||||
func Run(ctx context.Context, client *kube.Client, checkFilter CheckFilter, diagnosticFilter DiagnosticFilter) ([]Diagnostic, error) {
|
||||
objects, err := client.FetchObjects(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -17,10 +17,12 @@ limitations under the License.
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/digitalocean/clusterlint/checks"
|
||||
"github.com/digitalocean/clusterlint/kube"
|
||||
|
@ -34,7 +36,7 @@ import (
|
|||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "clusterlint"
|
||||
app.Usage = "Linter for k8sobjects from a live cluster"
|
||||
app.Usage = "Linter for k8s objects from a live cluster"
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "kubeconfig",
|
||||
|
@ -45,6 +47,11 @@ func main() {
|
|||
Name: "context",
|
||||
Usage: "context for the kubernetes client. default: current context",
|
||||
},
|
||||
cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
Usage: "configure timeout for the kubernetes client. default: 30s",
|
||||
Value: time.Second * 30,
|
||||
},
|
||||
}
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
|
@ -126,7 +133,7 @@ func listChecks(c *cli.Context) error {
|
|||
|
||||
// runChecks runs all the checks based on the flags passed.
|
||||
func runChecks(c *cli.Context) error {
|
||||
client, err := kube.NewClient(c.GlobalString("kubeconfig"), c.GlobalString("context"))
|
||||
client, err := kube.NewClient(kube.WithConfigFile(c.GlobalString("kubeconfig")), kube.WithKubeContext(c.GlobalString("context")), kube.WithTimeout(c.GlobalDuration("timeout")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -138,7 +145,7 @@ func runChecks(c *cli.Context) error {
|
|||
|
||||
diagnosticFilter := checks.DiagnosticFilter{Severity: checks.Severity(c.String("level"))}
|
||||
|
||||
diagnostics, err := checks.Run(client, filter, diagnosticFilter)
|
||||
diagnostics, err := checks.Run(context.Background(), client, filter, diagnosticFilter)
|
||||
|
||||
write(diagnostics, c)
|
||||
|
||||
|
|
|
@ -17,6 +17,9 @@ limitations under the License.
|
|||
package kube
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
ar "k8s.io/api/admissionregistration/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
@ -57,7 +60,8 @@ type Client struct {
|
|||
}
|
||||
|
||||
// FetchObjects returns the objects from a Kubernetes cluster.
|
||||
func (c *Client) FetchObjects() (*Objects, error) {
|
||||
// ctx is currently unused during API calls. More info: https://github.com/kubernetes/community/pull/1166
|
||||
func (c *Client) FetchObjects(ctx context.Context) (*Objects, error) {
|
||||
client := c.KubeClient.CoreV1()
|
||||
admissionControllerClient := c.KubeClient.AdmissionregistrationV1beta1()
|
||||
opts := metav1.ListOptions{}
|
||||
|
@ -135,46 +139,43 @@ func (c *Client) FetchObjects() (*Objects, error) {
|
|||
}
|
||||
|
||||
// NewClient builds a kubernetes client to interact with the live cluster.
|
||||
// The kube config file path and the context must be specified for the client
|
||||
// The kube config file path or the kubeconfig yaml must be specified
|
||||
// If not specified, defaults are assumed - configPath: ~/.kube/config, configContext: current context
|
||||
func NewClient(configPath, configContext string) (*Client, error) {
|
||||
func NewClient(opts ...Option) (*Client, error) {
|
||||
defOpts := &options{}
|
||||
|
||||
for _, opt := range opts {
|
||||
if err := opt(defOpts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var config *rest.Config
|
||||
var err error
|
||||
if defOpts.yaml != nil && defOpts.path != "" {
|
||||
return nil, errors.New("cannot specify both yaml and kubeconfg file path")
|
||||
}
|
||||
|
||||
if configContext != "" {
|
||||
if defOpts.yaml != nil {
|
||||
config, err = clientcmd.RESTConfigFromKubeConfig(defOpts.yaml)
|
||||
} else if defOpts.path != "" {
|
||||
if defOpts.kubeContext != "" {
|
||||
config, err = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
|
||||
&clientcmd.ClientConfigLoadingRules{ExplicitPath: configPath},
|
||||
&clientcmd.ClientConfigLoadingRules{ExplicitPath: defOpts.path},
|
||||
&clientcmd.ConfigOverrides{
|
||||
CurrentContext: configContext,
|
||||
CurrentContext: defOpts.kubeContext,
|
||||
}).ClientConfig()
|
||||
} else {
|
||||
config, err = clientcmd.BuildConfigFromFlags("", configPath)
|
||||
config, err = clientcmd.BuildConfigFromFlags("", defOpts.path)
|
||||
}
|
||||
} else {
|
||||
err = errors.New("cannot authenticate Kubernetes API requests")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Client{
|
||||
KubeClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// BuildClient builds a kubernetes client from the yaml to interact with the live cluster.
|
||||
func BuildClient(yaml []byte) (*Client, error) {
|
||||
var config *rest.Config
|
||||
var err error
|
||||
|
||||
config, err = clientcmd.RESTConfigFromKubeConfig(yaml)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config.Timeout = defOpts.timeout
|
||||
client, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -17,6 +17,8 @@ limitations under the License.
|
|||
package kube
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -37,7 +39,7 @@ func TestFetchObjects(t *testing.T) {
|
|||
Labels: map[string]string{"doks_key": "bar"}},
|
||||
})
|
||||
|
||||
actual, err := api.FetchObjects()
|
||||
actual, err := api.FetchObjects(context.Background())
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NotNil(t, actual.Nodes)
|
||||
|
@ -56,3 +58,12 @@ func TestFetchObjects(t *testing.T) {
|
|||
assert.NotNil(t, actual.MutatingWebhookConfigurations)
|
||||
assert.NotNil(t, actual.SystemNamespace)
|
||||
}
|
||||
|
||||
func TestNewClientErrors(t *testing.T) {
|
||||
// test both yaml and filepath specified
|
||||
_, err := NewClient(WithConfigFile("some-path"), WithYaml([]byte("yaml")))
|
||||
assert.Equal(t, errors.New("cannot specify both yaml and kubeconfg file path"), err)
|
||||
// test no authentication mechanism
|
||||
_, err = NewClient()
|
||||
assert.Equal(t, errors.New("cannot authenticate Kubernetes API requests"), err)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
Copyright 2019 DigitalOcean
|
||||
|
||||
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 kube
|
||||
|
||||
import "time"
|
||||
|
||||
type options struct {
|
||||
path string
|
||||
kubeContext string
|
||||
yaml []byte
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// Option function that allows injecting options while building kube.Client.
|
||||
type Option func(*options) error
|
||||
|
||||
// WithConfigFile returns an Option injected with a config file path.
|
||||
func WithConfigFile(path string) Option {
|
||||
return func(o *options) error {
|
||||
o.path = path
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithKubeContext returns an Option injected with a kubernetes context.
|
||||
func WithKubeContext(kubeContext string) Option {
|
||||
return func(o *options) error {
|
||||
o.kubeContext = kubeContext
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithYaml returns an Option injected with a kubeconfig yaml.
|
||||
func WithYaml(yaml []byte) Option {
|
||||
return func(o *options) error {
|
||||
o.yaml = yaml
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithTimeout returns an Option injected with a timeout option while building client.
|
||||
func WithTimeout(t time.Duration) Option {
|
||||
return func(o *options) error {
|
||||
o.timeout = t
|
||||
return nil
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue