// applyPatch reads the latest version of the object, writes it to version, then attempts to merge // the changes onto it without conflict. If a conflict occurs jsonmerge.IsConflicting(err) is // true. The info object is mutated func applyPatch(delta *jsonmerge.Delta, info *resource.Info, version string) error { if err := info.Get(); err != nil { return patchError{err} } obj, err := resource.AsVersionedObject([]*resource.Info{info}, false, version) if err != nil { return patchError{err} } data, err := info.Mapping.Codec.Encode(obj) if err != nil { return patchError{err} } merged, err := delta.Apply(data) if err != nil { return patchError{err} } mergedObj, err := info.Mapping.Codec.Decode(merged) if err != nil { return patchError{err} } updated, err := resource.NewHelper(info.Client, info.Mapping).Replace(info.Namespace, info.Name, false, mergedObj) if err != nil { return err } info.Refresh(updated, true) return nil }
// save invokes the API to alter an object. The reporter passed to this method is the same returned by // the migration visitor method (for this type, transformImageReferences). It should return an error // if the input type cannot be saved. It returns migrate.ErrRecalculate if migration should be re-run // on the provided object. func (o *MigrateImageReferenceOptions) save(info *resource.Info, reporter migrate.Reporter) error { switch t := info.Object.(type) { case *imageapi.ImageStream: // update status first so that a subsequent spec update won't pull incorrect values if reporter.(imageChangeInfo).status { updated, err := o.Client.ImageStreams(t.Namespace).UpdateStatus(t) if err != nil { return migrate.DefaultRetriable(info, err) } info.Refresh(updated, true) return migrate.ErrRecalculate } if reporter.(imageChangeInfo).spec { updated, err := o.Client.ImageStreams(t.Namespace).Update(t) if err != nil { return migrate.DefaultRetriable(info, err) } info.Refresh(updated, true) } return nil default: if _, err := resource.NewHelper(info.Client, info.Mapping).Replace(info.Namespace, info.Name, false, info.Object); err != nil { return migrate.DefaultRetriable(info, err) } } return nil }
// createAndRefresh creates an object from input info and refreshes info with that object func createAndRefresh(info *resource.Info) error { obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object) if err != nil { return err } info.Refresh(obj, true) return nil }
func createAndRefresh(info *resource.Info) error { obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object) if err != nil { if errors.IsAlreadyExists(err) { glog.V(5).Infof("Object %s/%s already exists", info.Namespace, info.Name) return nil } return err } info.Refresh(obj, true) return nil }
// CreateResource creates the given resource on openshift // and returns an error, or nil if successful // // Usage: first parse the resource with Context.ParseResource // and then use the visitor pattern on the parsed resource: // r.Visit(CreateResource) func CreateResource(info *resource.Info) error { data, err := info.Mapping.Codec.Encode(info.Object) if err != nil { return err } obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, data) if err != nil { return err } info.Refresh(obj, true) return nil }
// waitForObjectDeletion refreshes the object, waiting until it is deleted, a timeout is reached, or // an error is encountered. It checks once a second. func waitForObjectDeletion(info *resource.Info, timeout time.Duration) error { copied := *info info = &copied // TODO: refactor Reaper so that we can pass the "wait" option into it, and then check for UID change. return wait.PollImmediate(objectDeletionWaitInterval, timeout, func() (bool, error) { switch err := info.Get(); { case err == nil: return false, nil case errors.IsNotFound(err): return true, nil default: return false, err } }) }
// DefaultRetriable adds retry information to the provided error, and will refresh the // info if the client info is stale. If the refresh fails the error is made fatal. // All other errors are left in their natural state - they will not be retried unless // they define a Temporary() method that returns true. func DefaultRetriable(info *resource.Info, err error) error { if err == nil { return nil } switch { case errors.IsMethodNotSupported(err): return ErrNotRetriable{err} case errors.IsConflict(err): if refreshErr := info.Get(); refreshErr != nil { return ErrNotRetriable{err} } return ErrRetriable{err} case errors.IsServerTimeout(err): return ErrRetriable{err} } return err }
// save invokes the API to alter an object. The reporter passed to this method is the same returned by // the migration visitor method (for this type, transformAPIStorage). It should return an error // if the input type cannot be saved. It returns migrate.ErrRecalculate if migration should be re-run // on the provided object. func (o *MigrateAPIStorageOptions) save(info *resource.Info, reporter migrate.Reporter) error { switch info.Object.(type) { // TODO: add any custom mutations necessary default: // load the body and save it back, without transformation to avoid losing fields get := info.Client.Get(). Resource(info.Mapping.Resource). NamespaceIfScoped(info.Namespace, info.Mapping.Scope.Name() == meta.RESTScopeNameNamespace). Name(info.Name).Do() data, err := get.Raw() if err != nil { return migrate.DefaultRetriable(info, err) } update := info.Client.Put(). Resource(info.Mapping.Resource). NamespaceIfScoped(info.Namespace, info.Mapping.Scope.Name() == meta.RESTScopeNameNamespace). Name(info.Name).Body(data). Do() if err := update.Error(); err != nil { return migrate.DefaultRetriable(info, err) } if oldObject, err := get.Get(); err == nil { info.Refresh(oldObject, true) oldVersion := info.ResourceVersion if object, err := update.Get(); err == nil { info.Refresh(object, true) if info.ResourceVersion == oldVersion { return migrate.ErrUnchanged } } else { glog.V(4).Infof("unable to calculate resource version: %v", err) } } else { glog.V(4).Infof("unable to calculate resource version: %v", err) } } return nil }