func atomicUpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { gun := vars["imageName"] s := ctx.Value("metaStore") store, ok := s.(storage.MetaStore) if !ok { return errors.ErrNoStorage.WithDetail(nil) } cryptoServiceVal := ctx.Value("cryptoService") cryptoService, ok := cryptoServiceVal.(signed.CryptoService) if !ok { return errors.ErrNoCryptoService.WithDetail(nil) } reader, err := r.MultipartReader() if err != nil { return errors.ErrMalformedUpload.WithDetail(nil) } var updates []storage.MetaUpdate for { part, err := reader.NextPart() if err == io.EOF { break } role := strings.TrimSuffix(part.FileName(), ".json") if role == "" { return errors.ErrNoFilename.WithDetail(nil) } else if !data.ValidRole(role) { return errors.ErrInvalidRole.WithDetail(role) } meta := &data.SignedMeta{} var input []byte inBuf := bytes.NewBuffer(input) dec := json.NewDecoder(io.TeeReader(part, inBuf)) err = dec.Decode(meta) if err != nil { return errors.ErrMalformedJSON.WithDetail(nil) } version := meta.Signed.Version updates = append(updates, storage.MetaUpdate{ Role: role, Version: version, Data: inBuf.Bytes(), }) } updates, err = validateUpdate(cryptoService, gun, updates, store) if err != nil { serializable, serializableError := validation.NewSerializableError(err) if serializableError != nil { return errors.ErrInvalidUpdate.WithDetail(nil) } return errors.ErrInvalidUpdate.WithDetail(serializable) } err = store.UpdateMany(gun, updates) if err != nil { return errors.ErrUpdating.WithDetail(nil) } return nil }
// If it's a 400, translateStatusToError attempts to parse the body into // an error. If successful (and a recognized error) that error is returned. func TestTranslateErrorsParse400Errors(t *testing.T) { origErr := validation.ErrBadRoot{Msg: "bad"} serialObj, err := validation.NewSerializableError(origErr) require.NoError(t, err) serialization, err := json.Marshal(serialObj) require.NoError(t, err) errorBody := bytes.NewBuffer([]byte(fmt.Sprintf( `{"errors": [{"otherstuff": "what", "detail": %s}]}`, string(serialization)))) errorResp := http.Response{ StatusCode: http.StatusBadRequest, Body: ioutil.NopCloser(errorBody), } finalError := translateStatusToError(&errorResp, "") require.Equal(t, origErr, finalError) }
func atomicUpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { gun := vars["imageName"] s := ctx.Value("metaStore") logger := ctxu.GetLoggerWithField(ctx, gun, "gun") store, ok := s.(storage.MetaStore) if !ok { logger.Error("500 POST unable to retrieve storage") return errors.ErrNoStorage.WithDetail(nil) } cryptoServiceVal := ctx.Value("cryptoService") cryptoService, ok := cryptoServiceVal.(signed.CryptoService) if !ok { logger.Error("500 POST unable to retrieve signing service") return errors.ErrNoCryptoService.WithDetail(nil) } reader, err := r.MultipartReader() if err != nil { return errors.ErrMalformedUpload.WithDetail(nil) } var updates []storage.MetaUpdate for { part, err := reader.NextPart() if err == io.EOF { break } role := strings.TrimSuffix(part.FileName(), ".json") if role == "" { return errors.ErrNoFilename.WithDetail(nil) } else if !data.ValidRole(role) { return errors.ErrInvalidRole.WithDetail(role) } meta := &data.SignedMeta{} var input []byte inBuf := bytes.NewBuffer(input) dec := json.NewDecoder(io.TeeReader(part, inBuf)) err = dec.Decode(meta) if err != nil { return errors.ErrMalformedJSON.WithDetail(nil) } version := meta.Signed.Version updates = append(updates, storage.MetaUpdate{ Role: role, Version: version, Data: inBuf.Bytes(), }) } updates, err = validateUpdate(cryptoService, gun, updates, store) if err != nil { serializable, serializableError := validation.NewSerializableError(err) if serializableError != nil { return errors.ErrInvalidUpdate.WithDetail(nil) } return errors.ErrInvalidUpdate.WithDetail(serializable) } err = store.UpdateMany(gun, updates) if err != nil { // If we have an old version error, surface to user with error code if _, ok := err.(storage.ErrOldVersion); ok { return errors.ErrOldVersion.WithDetail(err) } // More generic storage update error, possibly due to attempted rollback logger.Errorf("500 POST error applying update request: %v", err) return errors.ErrUpdating.WithDetail(nil) } return nil }