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
|
package basic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/digitalocean/clusterlint/checks"
|
"github.com/digitalocean/clusterlint/checks"
|
||||||
"github.com/digitalocean/clusterlint/kube"
|
"github.com/digitalocean/clusterlint/kube"
|
||||||
)
|
)
|
||||||
|
@ -50,7 +48,6 @@ func (b *barePodCheck) Run(objects *kube.Objects) ([]checks.Diagnostic, error) {
|
||||||
var diagnostics []checks.Diagnostic
|
var diagnostics []checks.Diagnostic
|
||||||
for _, pod := range objects.Pods.Items {
|
for _, pod := range objects.Pods.Items {
|
||||||
pod := pod
|
pod := pod
|
||||||
fmt.Println(pod.ObjectMeta.OwnerReferences)
|
|
||||||
if len(pod.ObjectMeta.OwnerReferences) == 0 {
|
if len(pod.ObjectMeta.OwnerReferences) == 0 {
|
||||||
d := checks.Diagnostic{
|
d := checks.Diagnostic{
|
||||||
Check: b.Name(),
|
Check: b.Name(),
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
package checks
|
package checks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -25,8 +26,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Run applies the filters and runs the resultant check list in parallel
|
// Run applies the filters and runs the resultant check list in parallel
|
||||||
func Run(client *kube.Client, checkFilter CheckFilter, diagnosticFilter DiagnosticFilter) ([]Diagnostic, error) {
|
func Run(ctx context.Context, client *kube.Client, checkFilter CheckFilter, diagnosticFilter DiagnosticFilter) ([]Diagnostic, error) {
|
||||||
objects, err := client.FetchObjects()
|
objects, err := client.FetchObjects(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,12 @@ limitations under the License.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/digitalocean/clusterlint/checks"
|
"github.com/digitalocean/clusterlint/checks"
|
||||||
"github.com/digitalocean/clusterlint/kube"
|
"github.com/digitalocean/clusterlint/kube"
|
||||||
|
@ -34,7 +36,7 @@ import (
|
||||||
func main() {
|
func main() {
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
app.Name = "clusterlint"
|
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{
|
app.Flags = []cli.Flag{
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "kubeconfig",
|
Name: "kubeconfig",
|
||||||
|
@ -45,6 +47,11 @@ func main() {
|
||||||
Name: "context",
|
Name: "context",
|
||||||
Usage: "context for the kubernetes client. default: current 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{
|
app.Commands = []cli.Command{
|
||||||
{
|
{
|
||||||
|
@ -126,7 +133,7 @@ func listChecks(c *cli.Context) error {
|
||||||
|
|
||||||
// runChecks runs all the checks based on the flags passed.
|
// runChecks runs all the checks based on the flags passed.
|
||||||
func runChecks(c *cli.Context) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -138,7 +145,7 @@ func runChecks(c *cli.Context) error {
|
||||||
|
|
||||||
diagnosticFilter := checks.DiagnosticFilter{Severity: checks.Severity(c.String("level"))}
|
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)
|
write(diagnostics, c)
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,9 @@ limitations under the License.
|
||||||
package kube
|
package kube
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
ar "k8s.io/api/admissionregistration/v1beta1"
|
ar "k8s.io/api/admissionregistration/v1beta1"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
@ -57,7 +60,8 @@ type Client struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchObjects returns the objects from a Kubernetes cluster.
|
// 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()
|
client := c.KubeClient.CoreV1()
|
||||||
admissionControllerClient := c.KubeClient.AdmissionregistrationV1beta1()
|
admissionControllerClient := c.KubeClient.AdmissionregistrationV1beta1()
|
||||||
opts := metav1.ListOptions{}
|
opts := metav1.ListOptions{}
|
||||||
|
@ -135,46 +139,43 @@ func (c *Client) FetchObjects() (*Objects, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient builds a kubernetes client to interact with the live cluster.
|
// 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
|
// 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 config *rest.Config
|
||||||
var err error
|
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.NewNonInteractiveDeferredLoadingClientConfig(
|
config, err = clientcmd.RESTConfigFromKubeConfig(defOpts.yaml)
|
||||||
&clientcmd.ClientConfigLoadingRules{ExplicitPath: configPath},
|
} else if defOpts.path != "" {
|
||||||
&clientcmd.ConfigOverrides{
|
if defOpts.kubeContext != "" {
|
||||||
CurrentContext: configContext,
|
config, err = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
|
||||||
}).ClientConfig()
|
&clientcmd.ClientConfigLoadingRules{ExplicitPath: defOpts.path},
|
||||||
|
&clientcmd.ConfigOverrides{
|
||||||
|
CurrentContext: defOpts.kubeContext,
|
||||||
|
}).ClientConfig()
|
||||||
|
} else {
|
||||||
|
config, err = clientcmd.BuildConfigFromFlags("", defOpts.path)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
config, err = clientcmd.BuildConfigFromFlags("", configPath)
|
err = errors.New("cannot authenticate Kubernetes API requests")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
config.Timeout = defOpts.timeout
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := kubernetes.NewForConfig(config)
|
client, err := kubernetes.NewForConfig(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -17,6 +17,8 @@ limitations under the License.
|
||||||
package kube
|
package kube
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -37,7 +39,7 @@ func TestFetchObjects(t *testing.T) {
|
||||||
Labels: map[string]string{"doks_key": "bar"}},
|
Labels: map[string]string{"doks_key": "bar"}},
|
||||||
})
|
})
|
||||||
|
|
||||||
actual, err := api.FetchObjects()
|
actual, err := api.FetchObjects(context.Background())
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.NotNil(t, actual.Nodes)
|
assert.NotNil(t, actual.Nodes)
|
||||||
|
@ -56,3 +58,12 @@ func TestFetchObjects(t *testing.T) {
|
||||||
assert.NotNil(t, actual.MutatingWebhookConfigurations)
|
assert.NotNil(t, actual.MutatingWebhookConfigurations)
|
||||||
assert.NotNil(t, actual.SystemNamespace)
|
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