158 lines
4.0 KiB
Go
158 lines
4.0 KiB
Go
package interpret
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
"k8s.io/cli-runtime/pkg/resource"
|
|
"k8s.io/kubectl/pkg/cmd/util"
|
|
|
|
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
|
|
"github.com/karmada-io/karmada/pkg/karmadactl/util/genericresource"
|
|
"github.com/karmada-io/karmada/pkg/resourceinterpreter/configurableinterpreter"
|
|
"github.com/karmada-io/karmada/pkg/util/interpreter"
|
|
)
|
|
|
|
func (o *Options) completeExecute(f util.Factory) []error {
|
|
var errs []error
|
|
if o.DesiredFile != "" {
|
|
o.DesiredResult = f.NewBuilder().
|
|
Unstructured().
|
|
FilenameParam(false, &resource.FilenameOptions{Filenames: []string{o.DesiredFile}}).
|
|
RequireObject(true).
|
|
Local().
|
|
Do()
|
|
errs = append(errs, o.DesiredResult.Err())
|
|
}
|
|
|
|
if o.ObservedFile != "" {
|
|
o.ObservedResult = f.NewBuilder().
|
|
Unstructured().
|
|
FilenameParam(false, &resource.FilenameOptions{Filenames: []string{o.ObservedFile}}).
|
|
RequireObject(true).
|
|
Local().
|
|
Do()
|
|
errs = append(errs, o.ObservedResult.Err())
|
|
}
|
|
|
|
if len(o.StatusFile) > 0 {
|
|
o.StatusResult = genericresource.NewBuilder().
|
|
Constructor(func() interface{} { return &workv1alpha2.AggregatedStatusItem{} }).
|
|
Filename(false, o.StatusFile).
|
|
Do()
|
|
errs = append(errs, o.StatusResult.Err())
|
|
}
|
|
return errs
|
|
}
|
|
|
|
func (o *Options) runExecute() error {
|
|
if o.Operation == "" {
|
|
return fmt.Errorf("operation is not set for executing")
|
|
}
|
|
|
|
customizations, err := o.getCustomizationObject()
|
|
if err != nil {
|
|
return fmt.Errorf("fail to get customization object: %v", err)
|
|
}
|
|
|
|
desired, err := getUnstructuredObjectFromResult(o.DesiredResult)
|
|
if err != nil {
|
|
return fmt.Errorf("fail to get desired object: %v", err)
|
|
}
|
|
|
|
observed, err := getUnstructuredObjectFromResult(o.ObservedResult)
|
|
if err != nil {
|
|
return fmt.Errorf("fail to get observed object: %v", err)
|
|
}
|
|
|
|
status, err := o.getAggregatedStatusItems()
|
|
if err != nil {
|
|
return fmt.Errorf("fail to get status items: %v", err)
|
|
}
|
|
|
|
args := interpreter.RuleArgs{
|
|
Desired: desired,
|
|
Observed: observed,
|
|
Status: status,
|
|
Replica: int64(o.DesiredReplica),
|
|
}
|
|
|
|
interpreter := configurableinterpreter.NewConfigurableInterpreter(nil)
|
|
interpreter.LoadConfig(customizations)
|
|
|
|
r := o.Rules.GetByOperation(o.Operation)
|
|
if r == nil {
|
|
// Shall never occur, because we validate it before.
|
|
return fmt.Errorf("operation %s is not supported. Use one of: %s", o.Operation, strings.Join(o.Rules.Names(), ", "))
|
|
}
|
|
result := r.Run(interpreter, args)
|
|
printExecuteResult(o.Out, o.ErrOut, r.Name(), result)
|
|
return nil
|
|
}
|
|
|
|
func printExecuteResult(w, errOut io.Writer, name string, result *interpreter.RuleResult) {
|
|
if result.Err != nil {
|
|
fmt.Fprintf(errOut, "Execute %s error: %v\n", name, result.Err)
|
|
return
|
|
}
|
|
|
|
for i, res := range result.Results {
|
|
func() {
|
|
fmt.Fprintln(w, "---")
|
|
fmt.Fprintf(w, "# [%v/%v] %s:\n", i+1, len(result.Results), res.Name)
|
|
if err := printObjectYaml(w, res.Value); err != nil {
|
|
fmt.Fprintf(errOut, "ERROR: %v\n", err)
|
|
}
|
|
}()
|
|
}
|
|
}
|
|
|
|
// MarshalJSON doesn't work for yaml encoder, so unstructured.Unstructured and runtime.RawExtension objects
|
|
// will be encoded into unexpected data.
|
|
// Example1:
|
|
//
|
|
// &unstructured.Unstructured{
|
|
// Object: map[string]interface{}{
|
|
// "foo": "bar"
|
|
// },
|
|
// }
|
|
//
|
|
// will be encoded into:
|
|
//
|
|
// Object:
|
|
// foo: bar
|
|
//
|
|
// Example2:
|
|
//
|
|
// &runtime.RawExtension{
|
|
// Raw: []byte("{}"),
|
|
// }
|
|
//
|
|
// will be encoded into:
|
|
//
|
|
// raw:
|
|
// - 123
|
|
// - 125
|
|
//
|
|
// Inspired from https://github.com/kubernetes/kubernetes/blob/8fb423bfabe0d53934cc94c154c7da2dc3ce1332/staging/src/k8s.io/kubectl/pkg/cmd/get/get.go#L781-L786
|
|
// we convert it to map[string]interface{} by json, then encode the converted object to yaml.
|
|
func printObjectYaml(w io.Writer, obj interface{}) error {
|
|
data, err := json.Marshal(obj)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var converted interface{}
|
|
err = json.Unmarshal(data, &converted)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
encoder := yaml.NewEncoder(w)
|
|
defer encoder.Close()
|
|
return encoder.Encode(converted)
|
|
}
|