func (s *SwaggerSchema) ValidateBytes(data []byte) error { var obj interface{} out, err := yaml.ToJSON(data) if err != nil { return err } data = out if err := json.Unmarshal(data, &obj); err != nil { return err } fields, ok := obj.(map[string]interface{}) if !ok { return fmt.Errorf("error in unmarshaling data %s", string(data)) } apiVersion := fields["apiVersion"] if apiVersion == nil { return fmt.Errorf("apiVersion not set") } kind := fields["kind"] if kind == nil { return fmt.Errorf("kind not set") } allErrs := s.ValidateObject(obj, apiVersion.(string), "", apiVersion.(string)+"."+kind.(string)) if len(allErrs) == 1 { return allErrs[0] } return errors.NewAggregate(allErrs) }
// stripComments will transform a YAML file into JSON, thus dropping any comments // in it. Note that if the given file has a syntax error, the transformation will // fail and we will manually drop all comments from the file. func stripComments(file []byte) []byte { stripped, err := yaml.ToJSON(file) if err != nil { stripped = manualStrip(file) } return stripped }
// Apply attempts to apply the changes described by Delta onto latest, // returning an error if the changes cannot be applied cleanly. // IsConflicting will be true if the changes overlap, otherwise a // generic error will be returned. func (d *Delta) Apply(latest []byte) ([]byte, error) { base, err := yaml.ToJSON(latest) if err != nil { return nil, err } changes, err := jsonpatch.CreateMergePatch(d.original, base) if err != nil { return nil, err } diff1 := make(map[string]interface{}) if err := json.Unmarshal(d.edit, &diff1); err != nil { return nil, err } diff2 := make(map[string]interface{}) if err := json.Unmarshal(changes, &diff2); err != nil { return nil, err } for _, fn := range d.preconditions { hold1, _ := fn(diff1) hold2, _ := fn(diff2) if !hold1 || !hold2 { return nil, ErrPreconditionFailed } } glog.V(6).Infof("Testing for conflict between:\n%s\n%s", string(d.edit), string(changes)) if hasConflicts(diff1, diff2) { return nil, ErrConflict } return jsonpatch.MergePatch(base, d.edit) }
func walkJSONFiles(inDir string, fn func(name, path string, data []byte)) error { return filepath.Walk(inDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() && path != inDir { return filepath.SkipDir } file := filepath.Base(path) if ext := filepath.Ext(file); ext == ".json" || ext == ".yaml" { glog.Infof("Testing %s", path) data, err := ioutil.ReadFile(path) if err != nil { return err } name := strings.TrimSuffix(file, ext) if ext == ".yaml" { out, err := yaml.ToJSON(data) if err != nil { return fmt.Errorf("%s: %v", path, err) } data = out } fn(name, path, data) } return nil }) }
func (c yamlCodec) DecodeInto(data []byte, obj Object) error { out, err := yaml.ToJSON(data) if err != nil { return err } data = out return c.Codec.DecodeInto(data, obj) }
func (c yamlCodec) Decode(data []byte) (Object, error) { out, err := yaml.ToJSON(data) if err != nil { return nil, err } data = out return c.Codec.Decode(data) }
// NewDelta accepts two JSON or YAML documents and calculates the difference // between them. It returns a Delta object which can be used to resolve // conflicts against a third version with a common parent, or an error // if either document is in error. func NewDelta(from, to []byte) (*Delta, error) { d := &Delta{} before, err := yaml.ToJSON(from) if err != nil { return nil, err } after, err := yaml.ToJSON(to) if err != nil { return nil, err } diff, err := jsonpatch.CreateMergePatch(before, after) if err != nil { return nil, err } glog.V(6).Infof("Patch created from:\n%s\n%s\n%s", string(before), string(after), string(diff)) d.original = before d.edit = diff return d, nil }
// svcFromManifest reads a .json/yaml file and returns the rc in it. func svcFromManifest(fileName string) *api.Service { var svc api.Service Logf("Parsing service from %v", fileName) data, err := ioutil.ReadFile(fileName) Expect(err).NotTo(HaveOccurred()) json, err := utilyaml.ToJSON(data) Expect(err).NotTo(HaveOccurred()) Expect(api.Scheme.DecodeInto(json, &svc)).NotTo(HaveOccurred()) return &svc }
// rcFromManifest reads a .json/yaml file and returns the rc in it. func rcFromManifest(fileName string) *api.ReplicationController { var controller api.ReplicationController Logf("Parsing rc from %v", fileName) data, err := ioutil.ReadFile(fileName) Expect(err).NotTo(HaveOccurred()) json, err := utilyaml.ToJSON(data) Expect(err).NotTo(HaveOccurred()) Expect(api.Scheme.DecodeInto(json, &controller)).NotTo(HaveOccurred()) return &controller }
func ValidateSchema(data []byte, schema validation.Schema) error { if schema == nil { return nil } data, err := yaml.ToJSON(data) if err != nil { return fmt.Errorf("error converting to YAML: %v", err) } if err := schema.ValidateBytes(data); err != nil { return fmt.Errorf("error validating data: %v; %s", err, stopValidateMessage) } return nil }
func RunPatch(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, shortOutput bool, options *PatchOptions) error { cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err } patch := cmdutil.GetFlagString(cmd, "patch") if len(patch) == 0 { return cmdutil.UsageError(cmd, "Must specify -p to patch") } patchBytes, err := yaml.ToJSON([]byte(patch)) if err != nil { return fmt.Errorf("unable to parse %q: %v", patch, err) } mapper, typer := f.Object() r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Filenames...). ResourceTypeOrNameArgs(false, args...). Flatten(). Do() err = r.Err() if err != nil { return err } infos, err := r.Infos() if err != nil { return err } if len(infos) > 1 { return fmt.Errorf("multiple resources provided") } info := infos[0] name, namespace := info.Name, info.Namespace mapping := info.ResourceMapping() client, err := f.RESTClient(mapping) if err != nil { return err } helper := resource.NewHelper(client, mapping) _, err = helper.Patch(namespace, name, api.StrategicMergePatchType, patchBytes) if err != nil { return err } cmdutil.PrintSuccess(mapper, shortOutput, out, "", name, "patched") return nil }
// InfoForData creates an Info object for the given data. An error is returned // if any of the decoding or client lookup steps fail. Name and namespace will be // set into Info if the mapping's MetadataAccessor can retrieve them. func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) { json, err := yaml.ToJSON(data) if err != nil { return nil, fmt.Errorf("unable to parse %q: %v", source, err) } data = json version, kind, err := runtime.UnstructuredJSONScheme.DataVersionAndKind(data) if err != nil { return nil, fmt.Errorf("unable to get type info from %q: %v", source, err) } if ok := registered.IsRegisteredAPIVersion(version); !ok { return nil, fmt.Errorf("API version %q in %q isn't supported, only supports API versions %q", version, source, registered.RegisteredVersions) } if kind == "" { return nil, fmt.Errorf("kind not set in %q", source) } mapping, err := m.RESTMapping(kind, version) if err != nil { return nil, fmt.Errorf("unable to recognize %q: %v", source, err) } obj, err := mapping.Codec.Decode(data) if err != nil { return nil, fmt.Errorf("unable to load %q: %v", source, err) } client, err := m.ClientForMapping(mapping) if err != nil { return nil, fmt.Errorf("unable to connect to a server to handle %q: %v", mapping.Resource, err) } name, _ := mapping.MetadataAccessor.Name(obj) namespace, _ := mapping.MetadataAccessor.Namespace(obj) resourceVersion, _ := mapping.MetadataAccessor.ResourceVersion(obj) var versionedObject interface{} if vo, _, _, err := api.Scheme.Raw().DecodeToVersionedObject(data); err == nil { versionedObject = vo } return &Info{ Mapping: mapping, Client: client, Namespace: namespace, Name: name, Source: source, VersionedObject: versionedObject, Object: obj, ResourceVersion: resourceVersion, }, nil }
// AddObjectsFromPath loads the JSON or YAML file containing Kubernetes API resources // and adds them to the provided ObjectRetriever. func AddObjectsFromPath(path string, o ObjectRetriever, decoder runtime.Decoder) error { data, err := ioutil.ReadFile(path) if err != nil { return err } data, err = yaml.ToJSON(data) if err != nil { return err } obj, err := decoder.Decode(data) if err != nil { return err } if err := o.Add(obj); err != nil { return err } return nil }
func (s *SwaggerSchema) ValidateBytes(data []byte) error { var obj interface{} out, err := yaml.ToJSON(data) if err != nil { return err } data = out if err := json.Unmarshal(data, &obj); err != nil { return err } fields, ok := obj.(map[string]interface{}) if !ok { return fmt.Errorf("error in unmarshaling data %s", string(data)) } groupVersion := fields["apiVersion"] if groupVersion == nil { return fmt.Errorf("apiVersion not set") } if _, ok := groupVersion.(string); !ok { return fmt.Errorf("apiVersion isn't string type") } kind := fields["kind"] if kind == nil { return fmt.Errorf("kind not set") } if _, ok := kind.(string); !ok { return fmt.Errorf("kind isn't string type") } if strings.HasSuffix(kind.(string), "List") { return utilerrors.NewAggregate(s.validateList(fields)) } version := apiutil.GetVersion(groupVersion.(string)) allErrs := s.ValidateObject(obj, "", version+"."+kind.(string)) if len(allErrs) == 1 { return allErrs[0] } return utilerrors.NewAggregate(allErrs) }
func RunEdit(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, filenames []string) error { var printer kubectl.ResourcePrinter var ext string switch format := cmdutil.GetFlagString(cmd, "output"); format { case "json": printer = &kubectl.JSONPrinter{} ext = ".json" case "yaml": printer = &kubectl.YAMLPrinter{} ext = ".yaml" default: return cmdutil.UsageError(cmd, "The flag 'output' must be one of yaml|json") } cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err } mapper, typer := f.Object() rmap := &resource.Mapper{ ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand(), } r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, filenames...). ResourceTypeOrNameArgs(true, args...). Latest(). Flatten(). Do() err = r.Err() if err != nil { return err } infos, err := r.Infos() if err != nil { return err } clientConfig, err := f.ClientConfig() if err != nil { return err } defaultVersion := cmdutil.OutputVersion(cmd, clientConfig.Version) results := editResults{} for { objs, err := resource.AsVersionedObjects(infos, defaultVersion) if err != nil { return preservedFile(err, results.file, out) } // if input object is a list, traverse and edit each item one at a time for _, obj := range objs { // TODO: add an annotating YAML printer that can print inline comments on each field, // including descriptions or validation errors // generate the file to edit buf := &bytes.Buffer{} if err := results.header.writeTo(buf); err != nil { return preservedFile(err, results.file, out) } if err := printer.PrintObj(obj, buf); err != nil { return preservedFile(err, results.file, out) } original := buf.Bytes() // launch the editor edit := editor.NewDefaultEditor() edited, file, err := edit.LaunchTempFile("kubectl-edit-", ext, buf) if err != nil { return preservedFile(err, results.file, out) } // cleanup any file from the previous pass if len(results.file) > 0 { os.Remove(results.file) } glog.V(4).Infof("User edited:\n%s", string(edited)) lines, err := hasLines(bytes.NewBuffer(edited)) if err != nil { return preservedFile(err, file, out) } // Compare content without comments if bytes.Equal(stripComments(original), stripComments(edited)) { if len(results.edit) > 0 { preservedFile(nil, file, out) } else { os.Remove(file) } fmt.Fprintln(out, "Edit cancelled, no changes made.") continue } if !lines { if len(results.edit) > 0 { preservedFile(nil, file, out) } else { os.Remove(file) } fmt.Fprintln(out, "Edit cancelled, saved file was empty.") continue } results = editResults{ file: file, } // parse the edited file updates, err := rmap.InfoForData(edited, "edited-file") if err != nil { return fmt.Errorf("The edited file had a syntax error: %v", err) } // annotate the edited object for kubectl apply if err := kubectl.UpdateApplyAnnotation(updates); err != nil { return preservedFile(err, file, out) } visitor := resource.NewFlattenListVisitor(updates, rmap) // need to make sure the original namespace wasn't changed while editing if err = visitor.Visit(resource.RequireNamespace(cmdNamespace)); err != nil { return preservedFile(err, file, out) } // use strategic merge to create a patch originalJS, err := yaml.ToJSON(original) if err != nil { return preservedFile(err, file, out) } editedJS, err := yaml.ToJSON(edited) if err != nil { return preservedFile(err, file, out) } patch, err := strategicpatch.CreateStrategicMergePatch(originalJS, editedJS, obj) // TODO: change all jsonmerge to strategicpatch // for checking preconditions preconditions := []jsonmerge.PreconditionFunc{} if err != nil { glog.V(4).Infof("Unable to calculate diff, no merge is possible: %v", err) return preservedFile(err, file, out) } else { preconditions = append(preconditions, jsonmerge.RequireKeyUnchanged("apiVersion")) preconditions = append(preconditions, jsonmerge.RequireKeyUnchanged("kind")) preconditions = append(preconditions, jsonmerge.RequireMetadataKeyUnchanged("name")) results.version = defaultVersion } if hold, msg := jsonmerge.TestPreconditionsHold(patch, preconditions); !hold { fmt.Fprintf(out, "error: %s", msg) return preservedFile(nil, file, out) } err = visitor.Visit(func(info *resource.Info, err error) error { patched, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, api.StrategicMergePatchType, patch) if err != nil { fmt.Fprintln(out, results.addError(err, info)) return nil } info.Refresh(patched, true) cmdutil.PrintSuccess(mapper, false, out, info.Mapping.Resource, info.Name, "edited") return nil }) if err != nil { return preservedFile(err, file, out) } if results.retryable > 0 { fmt.Fprintf(out, "You can run `kubectl replace -f %s` to try this update again.\n", file) return errExit } if results.conflict > 0 { fmt.Fprintf(out, "You must update your local resource version and run `kubectl replace -f %s` to overwrite the remote changes.\n", file) return errExit } if len(results.edit) == 0 { if results.notfound == 0 { os.Remove(file) } else { fmt.Fprintf(out, "The edits you made on deleted resources have been saved to %q\n", file) } } } if len(results.edit) == 0 { return nil } // loop again and edit the remaining items infos = results.edit } return nil }
func TestReadme(t *testing.T) { paths := []struct { file string expectedType []runtime.Object }{ {"../README.md", []runtime.Object{&api.Pod{}}}, {"../docs/user-guide/walkthrough/README.md", []runtime.Object{&api.Pod{}}}, {"../examples/iscsi/README.md", []runtime.Object{&api.Pod{}}}, {"../docs/user-guide/simple-yaml.md", []runtime.Object{&api.Pod{}, &api.ReplicationController{}}}, } for _, path := range paths { data, err := ioutil.ReadFile(path.file) if err != nil { t.Errorf("Unable to read file %s: %v", path, err) continue } matches := sampleRegexp.FindAllStringSubmatch(string(data), -1) if matches == nil { continue } ix := 0 for _, match := range matches { var content, subtype string for i, name := range sampleRegexp.SubexpNames() { if name == "type" { subtype = match[i] } if name == "content" && match[i] != "" { content = match[i] } } if subtype == "yaml" && subsetRegexp.FindString(content) != "" { t.Logf("skipping (%s): \n%s", subtype, content) continue } var expectedType runtime.Object if len(path.expectedType) == 1 { expectedType = path.expectedType[0] } else { expectedType = path.expectedType[ix] ix++ } json, err := yaml.ToJSON([]byte(content)) if err != nil { t.Errorf("%s could not be converted to JSON: %v\n%s", path, err, string(content)) } if err := testapi.Default.Codec().DecodeInto(json, expectedType); err != nil { t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(content)) continue } if errors := validateObject(expectedType); len(errors) > 0 { t.Errorf("%s did not validate correctly: %v", path, errors) } _, err = testapi.Default.Codec().Encode(expectedType) if err != nil { t.Errorf("Could not encode object: %v", err) continue } } } }