Skip to content

Commit

Permalink
running kubectl apply in higher level
Browse files Browse the repository at this point in the history
  • Loading branch information
xuzhenglun committed Mar 28, 2022
1 parent 63888ac commit 61e9ef8
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 54 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/golang/glog v1.0.0
github.com/google/go-cmp v0.5.6
github.com/prometheus/client_golang v1.11.0
github.com/spf13/cobra v1.2.1
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff
k8s.io/api v0.23.0
Expand Down Expand Up @@ -80,7 +81,6 @@ require (
github.com/prometheus/procfs v0.6.0 // indirect
github.com/russross/blackfriday v1.5.2 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/spf13/cobra v1.2.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.7.0 // indirect
github.com/xanzy/ssh-agent v0.2.1 // indirect
Expand Down
136 changes: 83 additions & 53 deletions pkg/patterns/declarative/pkg/applier/direct.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
package applier

import (
"bytes"
"context"
"fmt"
"os"
"strings"

"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/printers"
"k8s.io/cli-runtime/pkg/resource"
"k8s.io/client-go/discovery"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/clientcmd/api"
"k8s.io/kubectl/pkg/cmd/apply"
cmdDelete "k8s.io/kubectl/pkg/cmd/delete"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"sigs.k8s.io/controller-runtime/pkg/log"
)

type DirectApplier struct {
Expand All @@ -27,78 +29,57 @@ func NewDirectApplier() *DirectApplier {
}

func (d *DirectApplier) Apply(ctx context.Context, opt ApplierOptions) error {
log := log.Log
ioReader := strings.NewReader(opt.Manifest)

buffer := &bytes.Buffer{}

ioStreams := genericclioptions.IOStreams{
In: os.Stdin,
Out: os.Stdout,
ErrOut: os.Stderr,
In: bytes.NewReader(nil),
Out: buffer,
ErrOut: buffer,
}
ioReader := strings.NewReader(opt.Manifest)

restClientGetter := &staticRESTClientGetter{
RESTMapper: opt.RESTMapper,
RESTConfig: opt.RESTConfig,
}
b := resource.NewBuilder(restClientGetter)

if opt.Validate {
// This potentially causes redundant work, but validation isn't the common path
v, err := cmdutil.NewFactory(&genericclioptions.ConfigFlags{}).Validator(true)
if err != nil {
return err
}
b.Schema(v)
}

res := b.Unstructured().Stream(ioReader, "manifestString").Do()
infos, err := res.Infos()
if err != nil {
return err
args := append(opt.ExtraArgs, "-f", "-")
if !opt.Validate {
args = append(args, "--validate=false")
}

// Populate the namespace on any namespace-scoped objects
if opt.Namespace != "" {
visitor := resource.SetNamespace(opt.Namespace)
for _, info := range infos {
if err := info.Visit(visitor); err != nil {
return fmt.Errorf("error from SetNamespace: %w", err)
}
}
}
log.V(4).Info("applying manifests", "args", args, "manifests", opt.Manifest)

applyOpts := apply.NewApplyOptions(ioStreams)

for i, arg := range opt.ExtraArgs {
switch arg {
case "--force":
applyOpts.ForceConflicts = true
case "--prune":
applyOpts.Prune = true
case "--selector":
applyOpts.Selector = opt.ExtraArgs[i+1]
default:
continue
}
cmd, o := NewCmdApply(ioStreams)
if err := cmd.ParseFlags(args); err != nil {
return fmt.Errorf("parse kubectl apply args failed, args: %v, errors: %w", args, err)
}

applyOpts.Namespace = opt.Namespace
applyOpts.SetObjects(infos)
applyOpts.ToPrinter = func(operation string) (printers.ResourcePrinter, error) {
applyOpts.PrintFlags.NamePrintFlags.Operation = operation
cmdutil.PrintFlagsWithDryRunStrategy(applyOpts.PrintFlags, applyOpts.DryRunStrategy)
return applyOpts.PrintFlags.ToPrinter()
if err := o.Complete(cmdutil.NewFactory(restClientGetter), cmd); err != nil {
return fmt.Errorf("apply manifests failed, args: %v, errors: %s, msg:%w", args, err)
}
res := o.Builder.Unstructured().Stream(ioReader, "manifestString").Do()
if infos, err := res.Infos(); err != nil {
return err
} else {
o.SetObjects(infos)
}
applyOpts.DeleteOptions = &cmdDelete.DeleteOptions{
IOStreams: ioStreams,

if err := o.Run(); err != nil {
return fmt.Errorf("apply manifests failed, args: %v, errors: %s, msg:%s", args, err, buffer.String())
}

return applyOpts.Run()
log.Info("applying manifest succeed", "message", buffer.String())
return nil
}

// staticRESTClientGetter returns a fixed RESTClient
type staticRESTClientGetter struct {
RESTConfig *rest.Config
DiscoveryClient discovery.CachedDiscoveryInterface
RESTMapper meta.RESTMapper
namespace string
}

var _ resource.RESTClientGetter = &staticRESTClientGetter{}
Expand All @@ -121,3 +102,52 @@ func (s *staticRESTClientGetter) ToRESTMapper() (meta.RESTMapper, error) {
}
return s.RESTMapper, nil
}
func (s *staticRESTClientGetter) ToRawKubeConfigLoader() clientcmd.ClientConfig {
return namespaceStub(s.namespace)
}

type namespaceStub string

func (n namespaceStub) Namespace() (string, bool, error) {
return string(n), false, nil
}

// below methods should never be called
func (namespaceStub) RawConfig() (api.Config, error) {
panic("implement me")
}

func (namespaceStub) ClientConfig() (*rest.Config, error) {
panic("implement me")
}

func (namespaceStub) ConfigAccess() clientcmd.ConfigAccess {
panic("implement me")
}

func NewCmdApply(ioStreams genericclioptions.IOStreams) (*cobra.Command, *apply.ApplyOptions) {
o := apply.NewApplyOptions(ioStreams)

// Store baseName for use in printing warnings / messages involving the base command name.
// This is useful for downstream command that wrap this one.

cmd := &cobra.Command{}

// bind flag structs
o.DeleteFlags.AddFlags(cmd)
o.RecordFlags.AddFlags(cmd)
o.PrintFlags.AddFlags(cmd)

cmd.Flags().BoolVar(&o.Overwrite, "overwrite", o.Overwrite, "Automatically resolve conflicts between the modified and live configuration by using values from the modified configuration")
cmd.Flags().BoolVar(&o.Prune, "prune", o.Prune, "Automatically delete resource objects, including the uninitialized ones, that do not appear in the configs and are created by either apply or create --save-config. Should be used with either -l or --all.")
cmdutil.AddValidateFlags(cmd)
cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
cmd.Flags().BoolVar(&o.All, "all", o.All, "Select all resources in the namespace of the specified resource types.")
cmd.Flags().StringArrayVar(&o.PruneWhitelist, "prune-whitelist", o.PruneWhitelist, "Overwrite the default whitelist with <group/version/kind> for --prune")
cmd.Flags().BoolVar(&o.OpenAPIPatch, "openapi-patch", o.OpenAPIPatch, "If true, use openapi to calculate diff when the openapi presents and the resource can be found in the openapi spec. Otherwise, fall back to use baked-in types.")
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddServerSideApplyFlags(cmd)
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, apply.FieldManagerClientSideApply)

return cmd, o
}

0 comments on commit 61e9ef8

Please sign in to comment.