Skip to content

Commit

Permalink
feat: use temporary KUBECONFIG (#924)
Browse files Browse the repository at this point in the history
Signed-off-by: Charles-Edouard Brétéché <[email protected]>
  • Loading branch information
eddycharly authored Feb 19, 2024
1 parent 401f18c commit d2201df
Show file tree
Hide file tree
Showing 14 changed files with 208 additions and 42 deletions.
46 changes: 39 additions & 7 deletions pkg/runner/operations/command/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"os"
"os/exec"
"path/filepath"

"github.com/jmespath-community/go-jmespath/pkg/binding"
"github.com/kyverno/chainsaw/pkg/apis/v1alpha1"
Expand All @@ -13,17 +14,26 @@ import (
"github.com/kyverno/chainsaw/pkg/runner/logging"
"github.com/kyverno/chainsaw/pkg/runner/operations"
"github.com/kyverno/chainsaw/pkg/runner/operations/internal"
restutils "github.com/kyverno/chainsaw/pkg/utils/rest"
"github.com/kyverno/kyverno/ext/output/color"
"k8s.io/client-go/rest"
)

type operation struct {
command v1alpha1.Command
basePath string
namespace string
bindings binding.Bindings
cfg *rest.Config
}

func New(command v1alpha1.Command, basePath string, namespace string, bindings binding.Bindings) operations.Operation {
func New(
command v1alpha1.Command,
basePath string,
namespace string,
bindings binding.Bindings,
cfg *rest.Config,
) operations.Operation {
if bindings == nil {
bindings = binding.NewBindings()
}
Expand All @@ -32,6 +42,7 @@ func New(command v1alpha1.Command, basePath string, namespace string, bindings b
basePath: basePath,
namespace: namespace,
bindings: bindings,
cfg: cfg,
}
}

Expand All @@ -40,29 +51,50 @@ func (o *operation) Exec(ctx context.Context) (_err error) {
defer func() {
internal.LogEnd(logger, logging.Command, _err)
}()
cmd, err := o.createCommand(ctx)
cmd, cancel, err := o.createCommand(ctx)
if cancel != nil {
defer cancel()
}
if err != nil {
return err
}
internal.LogStart(logger, logging.Command, logging.Section("COMMAND", cmd.String()))
return o.execute(ctx, cmd)
}

func (o *operation) createCommand(ctx context.Context) (*exec.Cmd, error) {
func (o *operation) createCommand(ctx context.Context) (*exec.Cmd, context.CancelFunc, error) {
var cancel context.CancelFunc
args := env.Expand(map[string]string{"NAMESPACE": o.namespace}, o.command.Args...)
cmd := exec.CommandContext(ctx, o.command.Entrypoint, args...) //nolint:gosec
env := os.Environ()
if cwd, err := os.Getwd(); err != nil {
return nil, fmt.Errorf("failed to get current working directory (%w)", err)
return nil, nil, fmt.Errorf("failed to get current working directory (%w)", err)
} else {
env = append(env, fmt.Sprintf("PATH=%s/bin/:%s", cwd, os.Getenv("PATH")))
}
env = append(env, fmt.Sprintf("NAMESPACE=%s", o.namespace))
// TODO
// env = append(env, fmt.Sprintf("KUBECONFIG=%s/bin/:%s", cwd, os.Getenv("PATH")))
if o.cfg != nil {
f, err := os.CreateTemp(o.basePath, "chainsaw-kubeconfig-")
if err != nil {
return nil, nil, err
}
path := f.Name()
cancel = func() {
err := os.Remove(path)
if err != nil {
logger := internal.GetLogger(ctx, nil)
logger.Log(logging.Script, logging.ErrorStatus, color.BoldYellow, logging.ErrSection(err))
}
}
defer f.Close()
if err := restutils.Save(o.cfg, f); err != nil {
return nil, cancel, err
}
env = append(env, fmt.Sprintf("KUBECONFIG=%s", filepath.Base(path)))
}
cmd.Env = env
cmd.Dir = o.basePath
return cmd, nil
return cmd, cancel, nil
}

func (o *operation) execute(ctx context.Context, cmd *exec.Cmd) error {
Expand Down
1 change: 1 addition & 0 deletions pkg/runner/operations/command/operation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ func Test_operationCommand(t *testing.T) {
tt.basePath,
tt.namespace,
nil,
nil,
)
err := operation.Exec(ctx)
if tt.wantErr {
Expand Down
39 changes: 33 additions & 6 deletions pkg/runner/operations/script/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,33 @@ import (
"fmt"
"os"
"os/exec"
"path/filepath"

"github.com/jmespath-community/go-jmespath/pkg/binding"
"github.com/kyverno/chainsaw/pkg/apis/v1alpha1"
"github.com/kyverno/chainsaw/pkg/runner/check"
"github.com/kyverno/chainsaw/pkg/runner/logging"
"github.com/kyverno/chainsaw/pkg/runner/operations"
"github.com/kyverno/chainsaw/pkg/runner/operations/internal"
restutils "github.com/kyverno/chainsaw/pkg/utils/rest"
"github.com/kyverno/kyverno/ext/output/color"
"k8s.io/client-go/rest"
)

type operation struct {
script v1alpha1.Script
basePath string
namespace string
bindings binding.Bindings
cfg *rest.Config
}

func New(
script v1alpha1.Script,
basePath string,
namespace string,
bindings binding.Bindings,
cfg *rest.Config,
) operations.Operation {
if bindings == nil {
bindings = binding.NewBindings()
Expand All @@ -36,6 +41,7 @@ func New(
basePath: basePath,
namespace: namespace,
bindings: bindings,
cfg: cfg,
}
}

Expand All @@ -44,28 +50,49 @@ func (o *operation) Exec(ctx context.Context) (err error) {
defer func() {
internal.LogEnd(logger, logging.Script, err)
}()
cmd, err := o.createCommand(ctx)
cmd, cancel, err := o.createCommand(ctx)
if cancel != nil {
defer cancel()
}
if err != nil {
return err
}
internal.LogStart(logger, logging.Script, logging.Section("COMMAND", cmd.String()))
return o.execute(ctx, cmd)
}

func (o *operation) createCommand(ctx context.Context) (*exec.Cmd, error) {
func (o *operation) createCommand(ctx context.Context) (*exec.Cmd, context.CancelFunc, error) {
var cancel context.CancelFunc
cmd := exec.CommandContext(ctx, "sh", "-c", o.script.Content) //nolint:gosec
env := os.Environ()
if cwd, err := os.Getwd(); err != nil {
return nil, fmt.Errorf("failed to get current working directory (%w)", err)
return nil, nil, fmt.Errorf("failed to get current working directory (%w)", err)
} else {
env = append(env, fmt.Sprintf("PATH=%s/bin/:%s", cwd, os.Getenv("PATH")))
}
env = append(env, fmt.Sprintf("NAMESPACE=%s", o.namespace))
// TODO
// env = append(env, fmt.Sprintf("KUBECONFIG=%s/bin/:%s", cwd, os.Getenv("PATH")))
if o.cfg != nil {
f, err := os.CreateTemp(o.basePath, "chainsaw-kubeconfig-")
if err != nil {
return nil, nil, err
}
path := f.Name()
cancel = func() {
err := os.Remove(path)
if err != nil {
logger := internal.GetLogger(ctx, nil)
logger.Log(logging.Script, logging.ErrorStatus, color.BoldYellow, logging.ErrSection(err))
}
}
defer f.Close()
if err := restutils.Save(o.cfg, f); err != nil {
return nil, cancel, err
}
env = append(env, fmt.Sprintf("KUBECONFIG=%s", filepath.Base(path)))
}
cmd.Env = env
cmd.Dir = o.basePath
return cmd, nil
return cmd, cancel, nil
}

func (o *operation) execute(ctx context.Context, cmd *exec.Cmd) error {
Expand Down
1 change: 1 addition & 0 deletions pkg/runner/operations/script/operation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ func Test_operationScript(t *testing.T) {
tt.basePath,
tt.namespace,
nil,
nil,
)
err := operation.Exec(ctx)
if tt.wantErr {
Expand Down
26 changes: 18 additions & 8 deletions pkg/runner/processors/clusters.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,40 @@ import (

const DefaultClient = ""

type cluster struct {
config *rest.Config
client client.Client
}

type clusters struct {
clients map[string]client.Client
clients map[string]cluster
}

func NewClusters() clusters {
return clusters{
clients: map[string]client.Client{},
clients: map[string]cluster{},
}
}

func (c *clusters) Register(name string, cfg *rest.Config) error {
client, err := client.New(cfg)
func (c *clusters) Register(name string, config *rest.Config) error {
client, err := client.New(config)
if err != nil {
return err
}
c.clients[DefaultClient] = runnerclient.New(client)
c.clients[DefaultClient] = cluster{
config: config,
client: runnerclient.New(client),
}
return nil
}

func (c *clusters) client(names ...string) client.Client {
func (c *clusters) client(names ...string) (*rest.Config, client.Client) {
for _, name := range names {
if name != "" {
return c.clients[name]
cluster := c.clients[name]
return cluster.config, cluster.client
}
}
return c.clients[DefaultClient]
cluster := c.clients[DefaultClient]
return cluster.config, cluster.client
}
32 changes: 18 additions & 14 deletions pkg/runner/processors/step.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/kyverno/chainsaw/pkg/testing"
"github.com/kyverno/kyverno/ext/output/color"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/client-go/rest"
"k8s.io/utils/clock"
)

Expand All @@ -51,6 +52,9 @@ func NewStepProcessor(
cleaner *cleaner,
bindings binding.Bindings,
) StepProcessor {
if bindings == nil {
bindings = binding.NewBindings()
}
return &stepProcessor{
config: config,
clusters: clusters,
Expand Down Expand Up @@ -318,7 +322,7 @@ func (p *stepProcessor) applyOperation(ctx context.Context, op v1alpha1.Apply) (
}
dryRun := op.DryRun != nil && *op.DryRun
template := template.Get(op.Template, p.step.Template, p.test.Spec.Template, p.config.Template)
cluster := p.getClient(op.Cluster, dryRun)
_, cluster := p.getClient(op.Cluster, dryRun)
bindings := p.bindings.Register("$client", binding.NewBinding(cluster))
for _, resource := range resources {
if err := p.prepareResource(resource); err != nil {
Expand All @@ -343,7 +347,7 @@ func (p *stepProcessor) assertOperation(ctx context.Context, op v1alpha1.Assert)
p.stepReport.AddOperation(operationReport)
}
template := template.Get(op.Template, p.step.Template, p.test.Spec.Template, p.config.Template)
cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster)
_, cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster)
bindings := p.bindings.Register("$client", binding.NewBinding(cluster))
for _, resource := range resources {
ops = append(ops, operation{
Expand All @@ -364,11 +368,11 @@ func (p *stepProcessor) commandOperation(ctx context.Context, op v1alpha1.Comman
if p.namespacer != nil {
ns = p.namespacer.GetNamespace()
}
cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster)
config, cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster)
bindings := p.bindings.Register("$client", binding.NewBinding(cluster))
return operation{
timeout: timeout.Get(op.Timeout, p.timeouts.ExecDuration()),
operation: opcommand.New(op, p.test.BasePath, ns, bindings),
operation: opcommand.New(op, p.test.BasePath, ns, bindings, config),
operationReport: operationReport,
}
}
Expand All @@ -385,7 +389,7 @@ func (p *stepProcessor) createOperation(ctx context.Context, op v1alpha1.Create)
}
dryRun := op.DryRun != nil && *op.DryRun
template := template.Get(op.Template, p.step.Template, p.test.Spec.Template, p.config.Template)
cluster := p.getClient(op.Cluster, dryRun)
_, cluster := p.getClient(op.Cluster, dryRun)
bindings := p.bindings.Register("$client", binding.NewBinding(cluster))
for _, resource := range resources {
if err := p.prepareResource(resource); err != nil {
Expand All @@ -411,7 +415,7 @@ func (p *stepProcessor) deleteOperation(ctx context.Context, op v1alpha1.Delete)
p.stepReport.AddOperation(operationReport)
}
template := template.Get(op.Template, p.step.Template, p.test.Spec.Template, p.config.Template)
cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster)
_, cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster)
bindings := p.bindings.Register("$client", binding.NewBinding(cluster))
return &operation{
timeout: timeout.Get(op.Timeout, p.timeouts.DeleteDuration()),
Expand All @@ -431,7 +435,7 @@ func (p *stepProcessor) errorOperation(ctx context.Context, op v1alpha1.Error) (
p.stepReport.AddOperation(operationReport)
}
template := template.Get(op.Template, p.step.Template, p.test.Spec.Template, p.config.Template)
cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster)
_, cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster)
bindings := p.bindings.Register("$client", binding.NewBinding(cluster))
for _, resource := range resources {
ops = append(ops, operation{
Expand All @@ -455,7 +459,7 @@ func (p *stepProcessor) patchOperation(ctx context.Context, op v1alpha1.Patch) (
}
dryRun := op.DryRun != nil && *op.DryRun
template := template.Get(op.Template, p.step.Template, p.test.Spec.Template, p.config.Template)
cluster := p.getClient(op.Cluster, dryRun)
_, cluster := p.getClient(op.Cluster, dryRun)
bindings := p.bindings.Register("$client", binding.NewBinding(cluster))
for _, resource := range resources {
if err := p.prepareResource(resource); err != nil {
Expand All @@ -478,11 +482,11 @@ func (p *stepProcessor) scriptOperation(ctx context.Context, op v1alpha1.Script)
if p.namespacer != nil {
ns = p.namespacer.GetNamespace()
}
cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster)
config, cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster)
bindings := p.bindings.Register("$client", binding.NewBinding(cluster))
return operation{
timeout: timeout.Get(op.Timeout, p.timeouts.ExecDuration()),
operation: opscript.New(op, p.test.BasePath, ns, bindings),
operation: opscript.New(op, p.test.BasePath, ns, bindings, config),
operationReport: operationReport,
}
}
Expand Down Expand Up @@ -559,12 +563,12 @@ func (p *stepProcessor) prepareResource(resource unstructured.Unstructured) erro
return nil
}

func (p *stepProcessor) getClient(opCluster string, dryRun bool) client.Client {
cluster := p.clusters.client(opCluster, p.step.Cluster, p.test.Spec.Cluster)
func (p *stepProcessor) getClient(opCluster string, dryRun bool) (*rest.Config, client.Client) {
config, cluster := p.clusters.client(opCluster, p.step.Cluster, p.test.Spec.Cluster)
if !dryRun {
return cluster
return config, cluster
}
return client.DryRun(cluster)
return config, client.DryRun(cluster)
}

func (p *stepProcessor) getCleaner(ctx context.Context, dryRun bool) cleanup.Cleaner {
Expand Down
4 changes: 3 additions & 1 deletion pkg/runner/processors/step_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1025,7 +1025,9 @@ func TestStepProcessor_Run(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
clusters := NewClusters()
if tc.client != nil {
clusters.clients[DefaultClient] = tc.client
clusters.clients[DefaultClient] = cluster{
client: tc.client,
}
}
stepProcessor := NewStepProcessor(
tc.config,
Expand Down
Loading

0 comments on commit d2201df

Please sign in to comment.