Updating an Environment Variable's scope via API fails
ZD ticket: https://gitlab.zendesk.com/agent/tickets/114826
Problem to Solve
- Have Premium or higher
- Create a project.
- Add 2 environment variables:
- key1, value1, scope: staging
- key1, value2, scope: production
- Try to update the value via the API:
- curl --request PUT --header "PRIVATE-TOKEN:
$$
$" HOST/api/v4/projects/7/variables/var_name --form "value=updatefromterm" --form "environment_scope=production" {"message":{"key":["(var_name) has already been taken"]}}
- curl --request PUT --header "PRIVATE-TOKEN:
Issues with put
It looks like our logic is bad in the API for put:
https://gitlab.com/gitlab-org/gitlab-ee/blob/master/lib/api/variables.rb#L83
We'll find the first instance of the key (find_by) and then we'll try and update it, but it fails validation because then it would match the "second" key in the scope:
Key1: value1, scope: scope1
Key2 value2, scope: scope2
find_by grabs key1 and says update it to:
Key1: newvalue, scope: scope2
but that conflicts because Key2 already exists with that scope.
Issues with delete
There is a similar problem with delete, where GitLab EE API supports CRUD operations on the Project Variables, however the DELETE action only takes "KEY" as argument. This means when have multiple Project Variables with the same KEY (just for a different environment scope), then the DELETE action deletes a random KEY.
Create variables:
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/variables" --form "key=NEW_VARIABLE" --form "value=new value"
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/variables" --form "key=NEW_VARIABLE" --form "value=new value" --form "environment_scope=test"
Delete a variable:
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/variables/NEW_VARIABLE"
Proposal
Require provision of the scope, especially when it is ambiguous.
By default use environment_scope="*" (to make it as seemless as possible to existing users), but if we do not find variable, we might print error message: we did not find, maybe you need to set environemnt_scope=env-name