// injectReactError returns an error when the test requested given action to // fail. nil is returned otherwise. func (r *volumeReactor) injectReactError(action core.Action) error { if len(r.errors) == 0 { // No more errors to inject, everything should succeed. return nil } for i, expected := range r.errors { glog.V(4).Infof("trying to match %q %q with %q %q", expected.verb, expected.resource, action.GetVerb(), action.GetResource()) if action.Matches(expected.verb, expected.resource) { // That's the action we're waiting for, remove it from injectedErrors r.errors = append(r.errors[:i], r.errors[i+1:]...) glog.V(4).Infof("reactor found matching error at index %d: %q %q, returning %v", i, expected.verb, expected.resource, expected.error) return expected.error } } return nil }
// React is a callback called by fake kubeClient from the controller. // In other words, every claim/volume change performed by the controller ends // here. // This callback checks versions of the updated objects and refuse those that // are too old (simulating real etcd). // All updated objects are stored locally to keep track of object versions and // to evaluate test results. // All updated objects are also inserted into changedObjects queue and // optionally sent back to the controller via its watchers. func (r *volumeReactor) React(action core.Action) (handled bool, ret runtime.Object, err error) { r.lock.Lock() defer r.lock.Unlock() glog.V(4).Infof("reactor got operation %q on %q", action.GetVerb(), action.GetResource()) // Inject error when requested err = r.injectReactError(action) if err != nil { return true, nil, err } // Test did not requst to inject an error, continue simulating API server. switch { case action.Matches("create", "persistentvolumes"): obj := action.(core.UpdateAction).GetObject() volume := obj.(*api.PersistentVolume) // check the volume does not exist _, found := r.volumes[volume.Name] if found { return true, nil, fmt.Errorf("Cannot create volume %s: volume already exists", volume.Name) } // Store the updated object to appropriate places. if r.volumeSource != nil { r.volumeSource.Add(volume) } r.volumes[volume.Name] = volume r.changedObjects = append(r.changedObjects, volume) r.changedSinceLastSync++ glog.V(4).Infof("created volume %s", volume.Name) return true, volume, nil case action.Matches("update", "persistentvolumes"): obj := action.(core.UpdateAction).GetObject() volume := obj.(*api.PersistentVolume) // Check and bump object version storedVolume, found := r.volumes[volume.Name] if found { storedVer, _ := strconv.Atoi(storedVolume.ResourceVersion) requestedVer, _ := strconv.Atoi(volume.ResourceVersion) if storedVer != requestedVer { return true, obj, versionConflictError } volume.ResourceVersion = strconv.Itoa(storedVer + 1) } else { return true, nil, fmt.Errorf("Cannot update volume %s: volume not found", volume.Name) } // Store the updated object to appropriate places. if r.volumeSource != nil { r.volumeSource.Modify(volume) } r.volumes[volume.Name] = volume r.changedObjects = append(r.changedObjects, volume) r.changedSinceLastSync++ glog.V(4).Infof("saved updated volume %s", volume.Name) return true, volume, nil case action.Matches("update", "persistentvolumeclaims"): obj := action.(core.UpdateAction).GetObject() claim := obj.(*api.PersistentVolumeClaim) // Check and bump object version storedClaim, found := r.claims[claim.Name] if found { storedVer, _ := strconv.Atoi(storedClaim.ResourceVersion) requestedVer, _ := strconv.Atoi(claim.ResourceVersion) if storedVer != requestedVer { return true, obj, versionConflictError } claim.ResourceVersion = strconv.Itoa(storedVer + 1) } else { return true, nil, fmt.Errorf("Cannot update claim %s: claim not found", claim.Name) } // Store the updated object to appropriate places. r.claims[claim.Name] = claim if r.claimSource != nil { r.claimSource.Modify(claim) } r.changedObjects = append(r.changedObjects, claim) r.changedSinceLastSync++ glog.V(4).Infof("saved updated claim %s", claim.Name) return true, claim, nil case action.Matches("get", "persistentvolumes"): name := action.(core.GetAction).GetName() volume, found := r.volumes[name] if found { glog.V(4).Infof("GetVolume: found %s", volume.Name) return true, volume, nil } else { glog.V(4).Infof("GetVolume: volume %s not found", name) return true, nil, fmt.Errorf("Cannot find volume %s", name) } case action.Matches("delete", "persistentvolumes"): name := action.(core.DeleteAction).GetName() glog.V(4).Infof("deleted volume %s", name) _, found := r.volumes[name] if found { delete(r.volumes, name) r.changedSinceLastSync++ return true, nil, nil } else { return true, nil, fmt.Errorf("Cannot delete volume %s: not found", name) } case action.Matches("delete", "persistentvolumeclaims"): name := action.(core.DeleteAction).GetName() glog.V(4).Infof("deleted claim %s", name) _, found := r.volumes[name] if found { delete(r.claims, name) r.changedSinceLastSync++ return true, nil, nil } else { return true, nil, fmt.Errorf("Cannot delete claim %s: not found", name) } } return false, nil, nil }