Management API Config Patch Method

I am just trying to update just an address in the intercept.v1 config. I am trying to use the Patch Method. I thought I only need to provide parameter(s) that I want to modify using the Patch Method. It does not seem to be the case.

I need to provide the portRanges and protocols for it to succeed even though I don’t need to change them. Is this correct?

The controller version is 0.30.0.

Documentation Reference used.

Yes, this is expected. I could imagine a --merge or similar option that lets you change specific fields in the configuration, but right now update is a wholesale replacement of the configuration json. The main advantage of using update on configurations is that you don’t need to re-associate an updated configuration with the services that use it.

Ok, what is the difference between PUT and PATCH? It looks like they do a similar function. What am I missing?

PATCH seems not to be working?

A PATCH operation should update a specified field. a PUT operation will update “all the fields” and you’ll have to supply all the json.
I just issued a curl to get my :

curl -sk -X GET -H "zt-session: $token" $mycontroller/edge/management/v1/configs | jq .

to get id: 5LwHA6zG0lxSPrENcThzeL.

Then I did a GET on that config:

curl -sk -X GET -H "zt-session: $token" $mycontroller/edge/management/v1/configs/5LwHA6zG0lxSPrENcThzeL | jq .data[]

which gives me valid json:

curl -sk -X GET -H "zt-session: $token" $baseurl/edge/management/v1/configs/5LwHA6zG0lxSPrENcThzeL | jq .
{
  "data": {
    "_links": {
      "self": {
        "href": "./configs/5LwHA6zG0lxSPrENcThzeL"
      }
    },
    "createdAt": "2023-08-20T03:12:06.977Z",
    "id": "5LwHA6zG0lxSPrENcThzeL",
    "tags": {},
    "updatedAt": "2023-08-20T03:12:06.977Z",
    "configType": {
      "_links": {
        "self": {
          "href": "./config-types/NH5p4FpGR"
        }
      },
      "entity": "config-types",
      "id": "NH5p4FpGR",
      "name": "host.v1"
    },
    "configTypeId": "NH5p4FpGR",
    "data": {
      "address": "apache-exporter",
      "listenOptions": {
        "bindUsingEdgeIdentity": true
      },
      "port": 9117,
      "protocol": "tcp"
    },
    "name": "prometheus.host.v1"
  },
  "meta": {}
}

So I tried to PATCH ‘address’:

curl -sk -X PATCH -H "Content-Type: application/json" -H "zt-session: $token" $mycontroller/edge/management/v1/configs/5LwHA6zG0lxSPrENcThzeL -d '{"data": {"address": "new_address"}}' | jq .

The controller returns:

{
  "error": {
    "cause": {
      "field": "(root)",
      "reason": "(root) is invalid: (root): Must validate \"else\" as \"if\" was not valid",
      "value": "map[address:new_address]"
    },
    "code": "COULD_NOT_VALIDATE",
    "message": "The supplied request contains an invalid document or no valid accept content were available, see cause",
    "requestId": "zOKuMNPmP"
  },
  "meta": {
    "apiEnrollmentVersion": "0.0.1",
    "apiVersion": "0.0.1"
  }
}

Using PUT Works

So I made a PUT.txt file (for easy use in curl):

{
    "createdAt": "2023-08-20T03:12:06.977Z",
    "id": "5LwHA6zG0lxSPrENcThzeL",
    "tags": {},
    "updatedAt": "2023-08-20T03:12:06.977Z",
    "configType": {
      "_links": {
        "self": {
          "href": "./config-types/NH5p4FpGR"
        }
      },
      "entity": "config-types",
      "id": "NH5p4FpGR",
      "name": "host.v1"
    },
    "configTypeId": "NH5p4FpGR",
    "data": {
      "address": "updated-apache-exporter",
      "listenOptions": {
        "bindUsingEdgeIdentity": true
      },
      "port": 9117,
      "protocol": "tcp"
    },
    "name": "prometheus.host.v1"
}

Then used curl to PUT that back:

curl -sk -X PUT -H "Content-Type: application/json" -H "zt-session: $token" $baseurl/edge/management/v1/configs/5LwHA6zG0lxSPrENcThzeL -d @PUT.txt

Afterwards, you can see my config address updated:

curl -sk -X GET -H "zt-session: $token" \
  $baseurl/edge/management/v1/configs/5LwHA6zG0lxSPrENcThzeL \
  | jq .data.data.address
"updated-apache-exporter"

I think we need @andrew.martinez to weigh on this one. PATCH wouldn’t work for me.

I recall there being some commotion for config PATCH, that I don't clearly recall. I think @plorenz might have been involved.

For the rest of the APIs, PUT is a full update, defaulting optional fields to their defaults and PATCH should only alter the values specified in the request payload. This should be kept consistent here, but as I said above there was a commotion that I don't clearly remember around this endpoint.

As an aside, PUT is also used in some APIs to allow full updates as well as create new resources at specific IDs. We don't support creating specific ids because it can cause issues with the internal database size/performance depending on how the ids are selected.

Patch works, you just need to provide a complete JSON document. PATCH lets you update individual fields and the config JSON is one field of the config. Just as you can't change just the third character of the config name, you can't change just one part of the config JSON. Does that make sense? If we did allow that, it wouldn't be PATCH semantics, it would be something config specific and we'd have to allow specifying how to merge the two JSON documents together.

Example:

plorenz@vimes:~/work/nf/ziti$ ziti edge create config foo host.v1 '{
      "address": "apache-exporter",
      "listenOptions": {
        "bindUsingEdgeIdentity": true
      },
      "port": 9117,
      "protocol": "tcp"
    }'
New config foo created with id: 7kwgRdBIfEaHAsYDFNJJwi
plorenz@vimes:~/work/nf/ziti$ ziti edge update config foo --output-request-json --data '{
      "address": "some-other-address",
      "listenOptions": {
        "bindUsingEdgeIdentity": true
      },
      "port": 9117,
      "protocol": "tcp"
    }'
PATCH to https://localhost:1280/edge/management/v1/configs/7kwgRdBIfEaHAsYDFNJJwi
{
    "data": {
        "address": "some-other-address",
        "listenOptions": {
            "bindUsingEdgeIdentity": true
        },
        "port": 9117,
        "protocol": "tcp"
    }
}

Cheers,
Paul

1 Like

This is very interesting discussion to me. If one researches about differences between PUT and PATCH, one can find a lof of conflicting views about this topic. The more I read the more I am confused about it.

With that being said, can you elaborate on this "PATCH lets you update individual fields and the config JSON is one field of the config". Especially the second part of the statement. Also this " it wouldn't be PATCH semantics"

As I posted at the beginning of this thread, I can use PUT and accomplish the same thing . I only changed one parameter in both requests. Why would one need to use PATCH if one can use PUT?

I often see this explanation when comparing PUT and PATCH

PATCH is used to apply partial updates to a resource, meaning that only the fields that need to be changed are sent in the request body. PUT is used to replace the entire resource with a new representation, meaning that all the fields of the resource are sent in the request body, even if they are not modified.

The exact intents for PUT and PATCH differ between API implementations. API development never circled the wagons on a protocol definition. Instead, a set of conventions has arisen.

It is simpler to take each API in stride. For OpenZiti, PATCH allows one to update specified fields on a resource. PUT replaces a resource, keeping its ID and other references. In most cases, PATCH will be used as it is more convenient for developers. PUT has limited applications where developers want to replace a resource fully, and they want their API call to fail if they aren't specifying all of the required fields.

1 Like