mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 09:22:44 +00:00
support editing before creating resource
This commit is contained in:
parent
192e18e2bb
commit
c641834bb3
@ -720,6 +720,25 @@ runTests() {
|
|||||||
# Pre-condition: no POD exists
|
# Pre-condition: no POD exists
|
||||||
create_and_use_new_namespace
|
create_and_use_new_namespace
|
||||||
kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" ''
|
kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" ''
|
||||||
|
## kubectl create --edit can update the image field of a POD. tmp-editor.sh is a fake editor
|
||||||
|
TEMP=$(mktemp /tmp/tmp-editor-XXXXXXXX.sh)
|
||||||
|
echo -e "#!/bin/bash\n$SED -i \"s/gcr.io\/google_containers\/serve_hostname/nginx/g\" \$1" > ${TEMP}
|
||||||
|
chmod +x ${TEMP}
|
||||||
|
# Command
|
||||||
|
EDITOR=${TEMP} kubectl create --edit -f test/fixtures/doc-yaml/admin/limitrange/valid-pod.yaml "${kube_flags[@]}"
|
||||||
|
# Post-condition: valid-pod POD is created and has image gcr.io/google_containers/serve_hostname
|
||||||
|
kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" 'valid-pod:'
|
||||||
|
kube::test::get_object_assert pods "{{range.items}}{{$image_field}}:{{end}}" 'nginx:'
|
||||||
|
# Clean up
|
||||||
|
rm ${TEMP}
|
||||||
|
kubectl delete pods/valid-pod "${kube_flags[@]}"
|
||||||
|
|
||||||
|
## kubectl create --edit won't create anything if user makes no changes
|
||||||
|
[ "$(EDITOR=cat kubectl create --edit -f test/fixtures/doc-yaml/admin/limitrange/valid-pod.yaml -o json 2>&1 | grep 'Edit cancelled')" ]
|
||||||
|
|
||||||
|
## Create valid-pod POD
|
||||||
|
# Pre-condition: no POD exists
|
||||||
|
kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" ''
|
||||||
# Command
|
# Command
|
||||||
kubectl create -f test/fixtures/doc-yaml/admin/limitrange/valid-pod.yaml "${kube_flags[@]}"
|
kubectl create -f test/fixtures/doc-yaml/admin/limitrange/valid-pod.yaml "${kube_flags[@]}"
|
||||||
# Post-condition: valid-pod POD is created
|
# Post-condition: valid-pod POD is created
|
||||||
|
@ -221,7 +221,7 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
|
|||||||
{
|
{
|
||||||
Message: "Basic Commands (Beginner):",
|
Message: "Basic Commands (Beginner):",
|
||||||
Commands: []*cobra.Command{
|
Commands: []*cobra.Command{
|
||||||
NewCmdCreate(f, out),
|
NewCmdCreate(f, out, err),
|
||||||
NewCmdExposeService(f, out),
|
NewCmdExposeService(f, out),
|
||||||
NewCmdRun(f, in, out, err),
|
NewCmdRun(f, in, out, err),
|
||||||
set.NewCmdSet(f, out, err),
|
set.NewCmdSet(f, out, err),
|
||||||
|
@ -19,6 +19,7 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
gruntime "runtime"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
@ -41,10 +42,13 @@ var (
|
|||||||
kubectl create -f ./pod.json
|
kubectl create -f ./pod.json
|
||||||
|
|
||||||
# Create a pod based on the JSON passed into stdin.
|
# Create a pod based on the JSON passed into stdin.
|
||||||
cat pod.json | kubectl create -f -`)
|
cat pod.json | kubectl create -f -
|
||||||
|
|
||||||
|
# Edit the data in docker-registry.yaml in JSON using the v1 API format then create the resource using the edited data.
|
||||||
|
kubectl create -f docker-registry.yaml --edit --output-version=v1 -o json`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewCmdCreate(f cmdutil.Factory, out io.Writer) *cobra.Command {
|
func NewCmdCreate(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
|
||||||
options := &resource.FilenameOptions{}
|
options := &resource.FilenameOptions{}
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@ -58,8 +62,7 @@ func NewCmdCreate(f cmdutil.Factory, out io.Writer) *cobra.Command {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
cmdutil.CheckErr(ValidateArgs(cmd, args))
|
cmdutil.CheckErr(ValidateArgs(cmd, args))
|
||||||
cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd))
|
cmdutil.CheckErr(RunCreate(f, cmd, out, errOut, options))
|
||||||
cmdutil.CheckErr(RunCreate(f, cmd, out, options))
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,6 +71,8 @@ func NewCmdCreate(f cmdutil.Factory, out io.Writer) *cobra.Command {
|
|||||||
cmd.MarkFlagRequired("filename")
|
cmd.MarkFlagRequired("filename")
|
||||||
cmdutil.AddValidateFlags(cmd)
|
cmdutil.AddValidateFlags(cmd)
|
||||||
cmdutil.AddPrinterFlags(cmd)
|
cmdutil.AddPrinterFlags(cmd)
|
||||||
|
cmd.Flags().Bool("edit", false, "Edit the API resource before creating")
|
||||||
|
cmd.Flags().Bool("windows-line-endings", gruntime.GOOS == "windows", "Only relevant if --edit=true. Use Windows line-endings (default Unix line-endings)")
|
||||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||||
cmdutil.AddRecordFlag(cmd)
|
cmdutil.AddRecordFlag(cmd)
|
||||||
cmdutil.AddDryRunFlag(cmd)
|
cmdutil.AddDryRunFlag(cmd)
|
||||||
@ -91,7 +96,10 @@ func ValidateArgs(cmd *cobra.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunCreate(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *resource.FilenameOptions) error {
|
func RunCreate(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, options *resource.FilenameOptions) error {
|
||||||
|
if cmdutil.GetFlagBool(cmd, "edit") {
|
||||||
|
return RunEditOnCreate(f, out, errOut, cmd, options)
|
||||||
|
}
|
||||||
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
|
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -159,6 +167,10 @@ func RunCreate(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *re
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RunEditOnCreate(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, options *resource.FilenameOptions) error {
|
||||||
|
return runEdit(f, out, errOut, cmd, []string{}, options, EditBeforeCreateMode)
|
||||||
|
}
|
||||||
|
|
||||||
// createAndRefresh creates an object from input info and refreshes info with that object
|
// createAndRefresh creates an object from input info and refreshes info with that object
|
||||||
func createAndRefresh(info *resource.Info) error {
|
func createAndRefresh(info *resource.Info) error {
|
||||||
obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object)
|
obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object)
|
||||||
|
@ -29,9 +29,10 @@ import (
|
|||||||
func TestExtraArgsFail(t *testing.T) {
|
func TestExtraArgsFail(t *testing.T) {
|
||||||
initTestErrorHandler(t)
|
initTestErrorHandler(t)
|
||||||
buf := bytes.NewBuffer([]byte{})
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
errBuf := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
f, _, _, _ := cmdtesting.NewAPIFactory()
|
f, _, _, _ := cmdtesting.NewAPIFactory()
|
||||||
c := NewCmdCreate(f, buf)
|
c := NewCmdCreate(f, buf, errBuf)
|
||||||
if ValidateArgs(c, []string{"rc"}) == nil {
|
if ValidateArgs(c, []string{"rc"}) == nil {
|
||||||
t.Errorf("unexpected non-error")
|
t.Errorf("unexpected non-error")
|
||||||
}
|
}
|
||||||
@ -59,8 +60,9 @@ func TestCreateObject(t *testing.T) {
|
|||||||
}
|
}
|
||||||
tf.Namespace = "test"
|
tf.Namespace = "test"
|
||||||
buf := bytes.NewBuffer([]byte{})
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
errBuf := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
cmd := NewCmdCreate(f, buf)
|
cmd := NewCmdCreate(f, buf, errBuf)
|
||||||
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy/redis-master-controller.yaml")
|
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy/redis-master-controller.yaml")
|
||||||
cmd.Flags().Set("output", "name")
|
cmd.Flags().Set("output", "name")
|
||||||
cmd.Run(cmd, []string{})
|
cmd.Run(cmd, []string{})
|
||||||
@ -94,8 +96,9 @@ func TestCreateMultipleObject(t *testing.T) {
|
|||||||
}
|
}
|
||||||
tf.Namespace = "test"
|
tf.Namespace = "test"
|
||||||
buf := bytes.NewBuffer([]byte{})
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
errBuf := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
cmd := NewCmdCreate(f, buf)
|
cmd := NewCmdCreate(f, buf, errBuf)
|
||||||
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy/redis-master-controller.yaml")
|
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy/redis-master-controller.yaml")
|
||||||
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.yaml")
|
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.yaml")
|
||||||
cmd.Flags().Set("output", "name")
|
cmd.Flags().Set("output", "name")
|
||||||
@ -129,8 +132,9 @@ func TestCreateDirectory(t *testing.T) {
|
|||||||
}
|
}
|
||||||
tf.Namespace = "test"
|
tf.Namespace = "test"
|
||||||
buf := bytes.NewBuffer([]byte{})
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
errBuf := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
cmd := NewCmdCreate(f, buf)
|
cmd := NewCmdCreate(f, buf, errBuf)
|
||||||
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy")
|
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy")
|
||||||
cmd.Flags().Set("output", "name")
|
cmd.Flags().Set("output", "name")
|
||||||
cmd.Run(cmd, []string{})
|
cmd.Run(cmd, []string{})
|
||||||
|
@ -118,12 +118,16 @@ func NewCmdEdit(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RunEdit(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args []string, options *resource.FilenameOptions) error {
|
func RunEdit(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args []string, options *resource.FilenameOptions) error {
|
||||||
|
return runEdit(f, out, errOut, cmd, args, options, NormalEditMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runEdit(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args []string, options *resource.FilenameOptions, editMode EditMode) error {
|
||||||
o, err := getPrinter(cmd)
|
o, err := getPrinter(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
mapper, resourceMapper, r, cmdNamespace, err := getMapperAndResult(f, args, options)
|
mapper, resourceMapper, r, cmdNamespace, err := getMapperAndResult(f, args, options, editMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -192,14 +196,15 @@ func RunEdit(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return preservedFile(err, results.file, errOut)
|
return preservedFile(err, results.file, errOut)
|
||||||
}
|
}
|
||||||
if bytes.Equal(stripComments(editedDiff), stripComments(edited)) {
|
if editMode == NormalEditMode || containsError {
|
||||||
// Ugly hack right here. We will hit this either (1) when we try to
|
if bytes.Equal(stripComments(editedDiff), stripComments(edited)) {
|
||||||
// save the same changes we tried to save in the previous iteration
|
// Ugly hack right here. We will hit this either (1) when we try to
|
||||||
// which means our changes are invalid or (2) when we exit the second
|
// save the same changes we tried to save in the previous iteration
|
||||||
// time. The second case is more usual so we can probably live with it.
|
// which means our changes are invalid or (2) when we exit the second
|
||||||
// TODO: A less hacky fix would be welcome :)
|
// time. The second case is more usual so we can probably live with it.
|
||||||
fmt.Fprintln(errOut, "Edit cancelled, no valid changes were saved.")
|
// TODO: A less hacky fix would be welcome :)
|
||||||
return nil
|
return preservedFile(fmt.Errorf("%s", "Edit cancelled, no valid changes were saved."), file, errOut)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanup any file from the previous pass
|
// cleanup any file from the previous pass
|
||||||
@ -229,6 +234,7 @@ func RunEdit(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args
|
|||||||
fmt.Fprintln(errOut, "Edit cancelled, no changes made.")
|
fmt.Fprintln(errOut, "Edit cancelled, no changes made.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
lines, err := hasLines(bytes.NewBuffer(edited))
|
lines, err := hasLines(bytes.NewBuffer(edited))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return preservedFile(err, file, errOut)
|
return preservedFile(err, file, errOut)
|
||||||
@ -271,7 +277,14 @@ func RunEdit(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args
|
|||||||
meta.SetList(updates.Object, mutatedObjects)
|
meta.SetList(updates.Object, mutatedObjects)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = visitToPatch(originalObj, updates, mapper, resourceMapper, encoder, out, errOut, defaultVersion, &results, file)
|
switch editMode {
|
||||||
|
case NormalEditMode:
|
||||||
|
err = visitToPatch(originalObj, updates, mapper, resourceMapper, encoder, out, errOut, defaultVersion, &results, file)
|
||||||
|
case EditBeforeCreateMode:
|
||||||
|
err = visitToCreate(updates, mapper, resourceMapper, out, errOut, defaultVersion, &results, file)
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("Not supported edit mode %q", editMode)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return preservedFile(err, results.file, errOut)
|
return preservedFile(err, results.file, errOut)
|
||||||
}
|
}
|
||||||
@ -315,7 +328,8 @@ func getPrinter(cmd *cobra.Command) (*editPrinterOptions, error) {
|
|||||||
ext: ".json",
|
ext: ".json",
|
||||||
addHeader: false,
|
addHeader: false,
|
||||||
}, nil
|
}, nil
|
||||||
case "yaml":
|
// If flag -o is not specified, use yaml as default
|
||||||
|
case "yaml", "":
|
||||||
return &editPrinterOptions{
|
return &editPrinterOptions{
|
||||||
printer: &kubectl.YAMLPrinter{},
|
printer: &kubectl.YAMLPrinter{},
|
||||||
ext: ".yaml",
|
ext: ".yaml",
|
||||||
@ -326,13 +340,24 @@ func getPrinter(cmd *cobra.Command) (*editPrinterOptions, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMapperAndResult(f cmdutil.Factory, args []string, options *resource.FilenameOptions) (meta.RESTMapper, *resource.Mapper, *resource.Result, string, error) {
|
func getMapperAndResult(f cmdutil.Factory, args []string, options *resource.FilenameOptions, editMode EditMode) (meta.RESTMapper, *resource.Mapper, *resource.Result, string, error) {
|
||||||
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
|
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, "", err
|
return nil, nil, nil, "", err
|
||||||
}
|
}
|
||||||
|
var mapper meta.RESTMapper
|
||||||
mapper, typer := f.Object()
|
var typer runtime.ObjectTyper
|
||||||
|
switch editMode {
|
||||||
|
case NormalEditMode:
|
||||||
|
mapper, typer = f.Object()
|
||||||
|
case EditBeforeCreateMode:
|
||||||
|
mapper, typer, err = f.UnstructuredObject()
|
||||||
|
default:
|
||||||
|
return nil, nil, nil, "", fmt.Errorf("Not supported edit mode %q", editMode)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, "", err
|
||||||
|
}
|
||||||
resourceMapper := &resource.Mapper{
|
resourceMapper := &resource.Mapper{
|
||||||
ObjectTyper: typer,
|
ObjectTyper: typer,
|
||||||
RESTMapper: mapper,
|
RESTMapper: mapper,
|
||||||
@ -345,14 +370,21 @@ func getMapperAndResult(f cmdutil.Factory, args []string, options *resource.File
|
|||||||
// compare two different GroupVersions).
|
// compare two different GroupVersions).
|
||||||
Decoder: f.Decoder(false),
|
Decoder: f.Decoder(false),
|
||||||
}
|
}
|
||||||
|
var b *resource.Builder
|
||||||
r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
|
switch editMode {
|
||||||
NamespaceParam(cmdNamespace).DefaultNamespace().
|
case NormalEditMode:
|
||||||
|
b = resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
|
||||||
|
ResourceTypeOrNameArgs(true, args...).
|
||||||
|
Latest()
|
||||||
|
case EditBeforeCreateMode:
|
||||||
|
b = resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.UnstructuredClientForMapping), runtime.UnstructuredJSONScheme)
|
||||||
|
default:
|
||||||
|
return nil, nil, nil, "", fmt.Errorf("Not supported edit mode %q", editMode)
|
||||||
|
}
|
||||||
|
r := b.NamespaceParam(cmdNamespace).DefaultNamespace().
|
||||||
FilenameParam(enforceNamespace, options).
|
FilenameParam(enforceNamespace, options).
|
||||||
ResourceTypeOrNameArgs(true, args...).
|
|
||||||
ContinueOnError().
|
ContinueOnError().
|
||||||
Flatten().
|
Flatten().
|
||||||
Latest().
|
|
||||||
Do()
|
Do()
|
||||||
err = r.Err()
|
err = r.Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -428,7 +460,7 @@ func visitToPatch(originalObj runtime.Object, updates *resource.Info, mapper met
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(4).Infof("Unable to calculate diff, no merge is possible: %v", err)
|
glog.V(4).Infof("Unable to calculate diff, no merge is possible: %v", err)
|
||||||
if strategicpatch.IsPreconditionFailed(err) {
|
if strategicpatch.IsPreconditionFailed(err) {
|
||||||
return preservedFile(nil, file, errOut)
|
return fmt.Errorf("%s", "At least one of apiVersion, kind and name was changed")
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -446,6 +478,19 @@ func visitToPatch(originalObj runtime.Object, updates *resource.Info, mapper met
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func visitToCreate(updates *resource.Info, mapper meta.RESTMapper, resourceMapper *resource.Mapper, out, errOut io.Writer, defaultVersion unversioned.GroupVersion, results *editResults, file string) error {
|
||||||
|
createVisitor := resource.NewFlattenListVisitor(updates, resourceMapper)
|
||||||
|
err := createVisitor.Visit(func(info *resource.Info, incomingErr error) error {
|
||||||
|
results.version = defaultVersion
|
||||||
|
if err := createAndRefresh(info); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cmdutil.PrintSuccess(mapper, false, out, info.Mapping.Resource, info.Name, false, "created")
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func visitAnnotation(cmd *cobra.Command, f cmdutil.Factory, updates *resource.Info, resourceMapper *resource.Mapper, encoder runtime.Encoder) ([]runtime.Object, error) {
|
func visitAnnotation(cmd *cobra.Command, f cmdutil.Factory, updates *resource.Info, resourceMapper *resource.Mapper, encoder runtime.Encoder) ([]runtime.Object, error) {
|
||||||
mutatedObjects := []runtime.Object{}
|
mutatedObjects := []runtime.Object{}
|
||||||
annotationVisitor := resource.NewFlattenListVisitor(updates, resourceMapper)
|
annotationVisitor := resource.NewFlattenListVisitor(updates, resourceMapper)
|
||||||
@ -468,6 +513,13 @@ func visitAnnotation(cmd *cobra.Command, f cmdutil.Factory, updates *resource.In
|
|||||||
return mutatedObjects, err
|
return mutatedObjects, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EditMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
NormalEditMode EditMode = "normal_mode"
|
||||||
|
EditBeforeCreateMode EditMode = "edit_before_create_mode"
|
||||||
|
)
|
||||||
|
|
||||||
// editReason preserves a message about the reason this file must be edited again
|
// editReason preserves a message about the reason this file must be edited again
|
||||||
type editReason struct {
|
type editReason struct {
|
||||||
head string
|
head string
|
||||||
|
Loading…
Reference in New Issue
Block a user