opened
palourde
username_0
### Description
We will introduce support for a new HTTP verb, `PATCH`, in 6.1. For now, it will be available exclusively via the REST API (sensuctl won't be able to use that yet). It's also available to "configuration" resources:

- apikeys
- checks
- clusterrolebindings
- clusterroles
- filters
- hooks
- mutators
- namespaces
- rolebindings
- roles

It's also available to entities, but only to the **configuration** attributes; behind the scenes, entities are now stored into two distinct resources, an [`EntityConfig`](https://github.com/sensu/sensu-go/blob/fe06dac0a08176a87f65bb1934613879d928b7ac/api/core/v3/entity_config.pb.go#L29) and an [`EntityState`](https://github.com/sensu/sensu-go/blob/fe06dac0a08176a87f65bb1934613879d928b7ac/api/core/v3/entity_state.pb.go#L31). Therefore, users will be able to PATCH entities attributes that belong to the `EntityConfig`.

The two notable exceptions in terms of resources are `silenced` entries and `users`. We might tackle those in a subsequent release.

For the moment, we only support `JSON Merge Patches`, as defined by the [RFC7396](https://tools.ietf.org/html/rfc7396), therefore users will have to explicitly set the `Content-Type` header to `application/merge-patch+json`.

Also, users won't be able to alter the `name` or the `namespace` of a resource via a PATCH request.

One important thing to note is that all GET and PATCH requests will now return an [`Etag`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) response header, which identifies a specific version of a resource.

Using that `Etag` value, users will be able to conditionally execute a PATCH request using the [`If-Match`]() and [`If-None-Match`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match) headers. When a PATCH request cannot be executed because one of the condition failed, we will return the response status code `412 Precondition Failed`.

Here are some useful examples:

### A user can add a label
```
$ curl -i -X PATCH \
-H "Authorization: Key $SENSU_API_KEY" \
-H 'Content-Type: application/merge-patch+json' \
-d '{
  "metadata": {
    "labels": {
      "what_magic": "is this"
    }
  }
}' \
http://127.0.0.1:8080/api/core/v2/namespaces/default/assets/sensu-go-hello-world

HTTP/1.1 200 OK
Content-Type: application/json
Etag: "deeb157828631797"
Date: Mon, 28 Sep 2020 13:56:43 GMT
Content-Length: 465

{"filters":["entity.system.os == linux"],"builds":[{"url":"https://self-signed.badssl.com/sensu-go-hello-world-0.0.1.tar.gz","sha512":"06665fea5b7c7ee15e7822820aa7ddb791cc6338e38444e976e601bc7d7970592e806a7b88733690a238b7325437d31f85e98ae2fe47b008ca09c86530da9600","filters":null,"headers":null}],"metadata":{"name":"sensu-go-hello-world","namespace":"default","labels":{"sensu.io/managed_by":"sensuctl","what_magic":"is this"},"created_by":"admin"},"headers":null}
```

### A user can replace an array
One of the caveat of JSON merge patches is that it's not possible to add elements to an array, you can only replace it.

```
$ curl -i -X PATCH \
-H "Authorization: Key $SENSU_API_KEY" \
-H 'Content-Type: application/merge-patch+json' \
-d '{
  "subscriptions": ["tootsie_roll"]
}' \
http://127.0.0.1:8080/api/core/v2/namespaces/default/entities/gin

HTTP/1.1 200 OK
Content-Type: application/json
Etag: "655cde6ef9f86805"
Date: Mon, 28 Sep 2020 13:58:48 GMT
Content-Length: 302

{"metadata":{"name":"gin","namespace":"default","labels":{"what_magic":"is this"}},"entity_class":"agent","user":"agent","subscriptions":["tootsie_roll"],"deregister":false,"deregistration":{},"redact":["password","passwd","pass","api_key","api_token","access_key","secret_key","private_key","secret"]}
```

### A user cannot alter the namespace or the name of a resource
```
$ curl -i -X PATCH \
-H "Authorization: Key $SENSU_API_KEY" \
-H 'Content-Type: application/merge-patch+json' \
-H 'Content-Type:' \
-d '{
  "metadata": {
    "namespace": "dev"
  }
}' \
http://127.0.0.1:8080/api/core/v2/namespaces/default/checks/check-asset

HTTP/1.1 400 Bad Request
X-Content-Type-Options: nosniff
Date: Mon, 28 Sep 2020 13:52:31 GMT
Content-Length: 108
Content-Type: text/plain; charset=utf-8

{"message":"the namespace of the resource (dev) does not match the namespace in the URI (default)","code":1}
```


### A PATCH request can only be issued if the stored resource matches the provided Etag in `If-Match`
```
$ curl -i -X PATCH \
-H "Authorization: Key $SENSU_API_KEY" \
-H 'Content-Type: application/merge-patch+json' \
-H 'If-Match: "deeb157828631797"' \
-d '{
  "metadata": {
    "labels": {
      "what_magic": "is this"
    }
  }
}' \
http://127.0.0.1:8080/api/core/v2/namespaces/default/assets/sensu-go-hello-world

HTTP/1.1 200 OK
Content-Type: application/json
Etag: "deeb157828631797"
Date: Mon, 28 Sep 2020 14:01:18 GMT
Content-Length: 465

{"filters":["entity.system.os == linux"],"builds":[{"url":"https://self-signed.badssl.com/sensu-go-hello-world-0.0.1.tar.gz","sha512":"06665fea5b7c7ee15e7822820aa7ddb791cc6338e38444e976e601bc7d7970592e806a7b88733690a238b7325437d31f85e98ae2fe47b008ca09c86530da9600","filters":null,"headers":null}],"metadata":{"name":"sensu-go-hello-world","namespace":"default","labels":{"sensu.io/managed_by":"sensuctl","what_magic":"is this"},"created_by":"admin"},"headers":null}
```

### A PATCH request can only be issued if the stored resource does not match any of the provided Etag in `If-Match`
```
$ curl -i -X PATCH \
-H "Authorization: Key $SENSU_API_KEY" \
-H 'Content-Type: application/merge-patch+json' \
-H 'If-None-Match: "2459df058c399716", "67ab43"' \
-d '{
  "metadata": {
    "labels": {
      "what_magic": "is this"
    }
  }
}' \
http://127.0.0.1:8080/api/core/v2/namespaces/default/entities/gin
HTTP/1.1 200 OK
Content-Type: application/json
Etag: "655cde6ef9f86805"
Date: Mon, 28 Sep 2020 14:02:05 GMT
Content-Length: 302

{"metadata":{"name":"gin","namespace":"default","labels":{"what_magic":"is this"}},"entity_class":"agent","user":"agent","subscriptions":["tootsie_roll"],"deregister":false,"deregistration":{},"redact":["password","passwd","pass","api_key","api_token","access_key","secret_key","private_key","secret"]}
```

### A 412 Precondition Failed error is returned if the request cannot be fulfilled because of the condition
```
$ curl -i -X PATCH \
-H "Authorization: Key $SENSU_API_KEY" \
-H 'Content-Type: application/merge-patch+json' \
-H 'If-Match: "54ed21"' \
-d '{
  "metadata": {
    "labels": {
      "what_magic": "is this"
    }
  }
}' \
http://127.0.0.1:8080/api/core/v2/namespaces/default/assets/sensu-go-hello-world

HTTP/1.1 412 Precondition Failed
X-Content-Type-Options: nosniff
Date: Mon, 28 Sep 2020 14:04:21 GMT
Content-Length: 110
Content-Type: text/plain; charset=utf-8

{"message":"at least one condition failed for the key /sensu.io/assets/default/sensu-go-hello-world","code":7}
```

### Only the entity configuration attributes can be PATCHED
See the returned response body, which only returns configuration attributes.
```
$ curl -i -X PATCH \
-H "Authorization: Key $SENSU_API_KEY" \
-H 'Content-Type: application/merge-patch+json' \
-d '{
  "deregister": true
}' \
http://127.0.0.1:8080/api/core/v2/namespaces/default/entities/gin

HTTP/1.1 200 OK
Content-Type: application/json
Etag: "655cde6ef9f86805"
Date: Mon, 28 Sep 2020 14:05:25 GMT
Content-Length: 302

{"metadata":{"name":"gin","namespace":"default","labels":{"what_magic":"is this"}},"entity_class":"agent","user":"agent","subscriptions":["tootsie_roll"],"deregister":false,"deregistration":{},"redact":["password","passwd","pass","api_key","api_token","access_key","secret_key","private_key","secret"]}
```

### Category

This is an issue with:

- [ ] errata (error in doc content)
- [x] new doc (requires a completely new doc)
- [ ] update (add missing or refresh existing content)
- [ ] improvement (improve docs content or format)


Please let me know if you have any question or you would like some specific examples.

Thanks!
Document PATCH method for the REST API
issue
closed
hillaryfraley
username_1
issue