mirror of https://github.com/kubernetes/kops.git
Give "edit instancegroup" parity with "edit cluster"
This commit is contained in:
parent
6eda65d9f7
commit
23478734ae
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
@ -31,6 +32,7 @@ import (
|
||||||
api "k8s.io/kops/pkg/apis/kops"
|
api "k8s.io/kops/pkg/apis/kops"
|
||||||
"k8s.io/kops/pkg/apis/kops/validation"
|
"k8s.io/kops/pkg/apis/kops/validation"
|
||||||
"k8s.io/kops/pkg/assets"
|
"k8s.io/kops/pkg/assets"
|
||||||
|
"k8s.io/kops/pkg/edit"
|
||||||
"k8s.io/kops/pkg/kopscodecs"
|
"k8s.io/kops/pkg/kopscodecs"
|
||||||
"k8s.io/kops/pkg/try"
|
"k8s.io/kops/pkg/try"
|
||||||
"k8s.io/kops/upup/pkg/fi/cloudup"
|
"k8s.io/kops/upup/pkg/fi/cloudup"
|
||||||
|
|
@ -126,7 +128,7 @@ func RunEditInstanceGroup(ctx context.Context, f *util.Factory, out io.Writer, o
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
edit = editor.NewDefaultEditor(editorEnvs)
|
editor = editor.NewDefaultEditor(editorEnvs)
|
||||||
)
|
)
|
||||||
|
|
||||||
ext := "yaml"
|
ext := "yaml"
|
||||||
|
|
@ -135,64 +137,142 @@ func RunEditInstanceGroup(ctx context.Context, f *util.Factory, out io.Writer, o
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// launch the editor
|
var (
|
||||||
edited, file, err := edit.LaunchTempFile(fmt.Sprintf("%s-edit-", filepath.Base(os.Args[0])), ext, bytes.NewReader(raw))
|
results = editResults{}
|
||||||
defer func() {
|
edited = []byte{}
|
||||||
if file != "" {
|
file string
|
||||||
try.RemoveFile(file)
|
)
|
||||||
}
|
|
||||||
}()
|
containsError := false
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error launching editor: %v", err)
|
for {
|
||||||
}
|
buf := &bytes.Buffer{}
|
||||||
|
results.header.writeTo(buf)
|
||||||
|
results.header.flush()
|
||||||
|
|
||||||
|
if !containsError {
|
||||||
|
buf.Write(raw)
|
||||||
|
} else {
|
||||||
|
buf.Write(stripComments(edited))
|
||||||
|
}
|
||||||
|
|
||||||
|
// launch the editor
|
||||||
|
editedDiff := edited
|
||||||
|
edited, file, err = editor.LaunchTempFile(fmt.Sprintf("%s-edit-", filepath.Base(os.Args[0])), ext, buf)
|
||||||
|
if err != nil {
|
||||||
|
return preservedFile(fmt.Errorf("error launching editor: %v", err), results.file, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
if containsError {
|
||||||
|
if bytes.Equal(stripComments(editedDiff), stripComments(edited)) {
|
||||||
|
return preservedFile(fmt.Errorf("%s", "Edit cancelled: no valid changes were saved."), file, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(results.file) > 0 {
|
||||||
|
try.RemoveFile(results.file)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Equal(stripComments(raw), stripComments(edited)) {
|
||||||
|
try.RemoveFile(file)
|
||||||
|
fmt.Fprintln(out, "Edit cancelled: no changes made.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lines, err := hasLines(bytes.NewBuffer(edited))
|
||||||
|
if err != nil {
|
||||||
|
return preservedFile(err, file, out)
|
||||||
|
}
|
||||||
|
if !lines {
|
||||||
|
try.RemoveFile(file)
|
||||||
|
fmt.Fprintln(out, "Edit cancelled: saved file was empty.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
newObj, _, err := kopscodecs.Decode(edited, nil)
|
||||||
|
if err != nil {
|
||||||
|
return preservedFile(fmt.Errorf("error parsing InstanceGroup: %v", err), file, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
newGroup, ok := newObj.(*api.InstanceGroup)
|
||||||
|
if !ok {
|
||||||
|
results = editResults{
|
||||||
|
file: file,
|
||||||
|
}
|
||||||
|
results.header.addError(fmt.Sprintf("object was not of expected type: %T", newObj))
|
||||||
|
containsError = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
extraFields, err := edit.HasExtraFields(string(edited), newObj)
|
||||||
|
if err != nil {
|
||||||
|
results = editResults{
|
||||||
|
file: file,
|
||||||
|
}
|
||||||
|
results.header.addError(fmt.Sprintf("error checking for extra fields: %v", err))
|
||||||
|
containsError = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if extraFields != "" {
|
||||||
|
results = editResults{
|
||||||
|
file: file,
|
||||||
|
}
|
||||||
|
lines := strings.Split(extraFields, "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
results.header.addExtraFields(line)
|
||||||
|
}
|
||||||
|
containsError = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
cloud, err := cloudup.BuildCloud(cluster)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fullGroup, err := cloudup.PopulateInstanceGroupSpec(cluster, newGroup, cloud, channel)
|
||||||
|
if err != nil {
|
||||||
|
results = editResults{
|
||||||
|
file: file,
|
||||||
|
}
|
||||||
|
results.header.addError(fmt.Sprintf("error populating instance group spec: %s", err))
|
||||||
|
containsError = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need the full cluster spec to perform deep validation
|
||||||
|
// Note that we don't write it back though
|
||||||
|
err = cloudup.PerformAssignments(cluster, cloud)
|
||||||
|
if err != nil {
|
||||||
|
return preservedFile(fmt.Errorf("error populating configuration: %v", err), file, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
assetBuilder := assets.NewAssetBuilder(cluster, false)
|
||||||
|
fullCluster, err := cloudup.PopulateClusterSpec(clientset, cluster, cloud, assetBuilder)
|
||||||
|
if err != nil {
|
||||||
|
results = editResults{
|
||||||
|
file: file,
|
||||||
|
}
|
||||||
|
results.header.addError(fmt.Sprintf("error populating cluster spec: %s", err))
|
||||||
|
containsError = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = validation.CrossValidateInstanceGroup(fullGroup, fullCluster, cloud).ToAggregate()
|
||||||
|
if err != nil {
|
||||||
|
results = editResults{
|
||||||
|
file: file,
|
||||||
|
}
|
||||||
|
results.header.addError(fmt.Sprintf("validation failed: %s", err))
|
||||||
|
containsError = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note we perform as much validation as we can, before writing a bad config
|
||||||
|
_, err = clientset.InstanceGroupsFor(cluster).Update(ctx, fullGroup, metav1.UpdateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return preservedFile(err, file, out)
|
||||||
|
}
|
||||||
|
|
||||||
if bytes.Equal(edited, raw) {
|
|
||||||
fmt.Fprintln(out, "Edit cancelled: no changes made.")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
newObj, _, err := kopscodecs.Decode(edited, nil)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error parsing InstanceGroup: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
newGroup, ok := newObj.(*api.InstanceGroup)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("object was not of expected type: %T", newObj)
|
|
||||||
}
|
|
||||||
|
|
||||||
cloud, err := cloudup.BuildCloud(cluster)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fullGroup, err := cloudup.PopulateInstanceGroupSpec(cluster, newGroup, cloud, channel)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need the full cluster spec to perform deep validation
|
|
||||||
// Note that we don't write it back though
|
|
||||||
err = cloudup.PerformAssignments(cluster, cloud)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error populating configuration: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
assetBuilder := assets.NewAssetBuilder(cluster, false)
|
|
||||||
fullCluster, err := cloudup.PopulateClusterSpec(clientset, cluster, cloud, assetBuilder)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = validation.CrossValidateInstanceGroup(fullGroup, fullCluster, cloud).ToAggregate()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note we perform as much validation as we can, before writing a bad config
|
|
||||||
_, err = clientset.InstanceGroupsFor(cluster).Update(ctx, fullGroup, metav1.UpdateOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue