Story #7127
Updated by daviddavis almost 4 years ago
## Proposal As a developer, it would be nice to be able to label pulp objects such as repositories. To accomplish this, I propose adding Kubernetes style labels to pulp objects (more information on kubernetes labels here: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/). Labels are arbitrary key value pairs that can be applied to objects and used to query them. For example, I could attach a `label1=foo`, `label2=foo`, and `label3=foobar` to a repository. The repository could then be queried by any of the following methods * `foo=bar`: select all objects that have the foo label set to bar * `foo!=bar`: select all objects that have the foo label set to anything except bar * `foo in (bar, foobar)`: selects all objects with the foo label that contain either the values for bar or foobar * `foo notin (bar, foobar)`: selects all objects with the foo label that don't contain bar or foobar * `foo`: selects all objects that contain the label foo (regardless of value) * `!foo`: selects all objects that don't contain the label foo ## Use cases In galaxy_ng we need a way to filter repositories based on what they are used for. For example we are going to have the following repositories * `published` - contains locally published collections. Should be searchable. Is the default repo if no repo is provided * `staging` - content waiting for approval. Not searchable * `rejected` - content that has been rejected. Not searchable * `rh-certified` - content synced from automation hub. Contains red hat certified content. Searchable * `community` - content synced from galaxy. Searchable As well as an arbitrary number of repositories named inbound-<namespace_name> for uploading collections. Not searchable. Right now the names for these repos are hard coded as a way for the API to identify which content should go where, however with label support we could add the following labels * searchable= true | false * default=true | false * content-readiness= production | inbound | staging * certification= community | rh-certified | local With these labels we can: * limit search results to repos that match `searchable=true` * separate inbound repos with `content-readiness!=inbound` * identify which repositories contain certified content with `certification=rh-certified` ## Design ### API Design #### Filtering Labels can be filtered by passing a urlencoded string to a `label_selector` parameter. Some examples based on [the kubernetes documentation](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api): * `?label_selector=environment%3Dproduction,tier%3Dfrontend` * Evaluates to `environment=production,tier=frontend` * `?label_selector=environment+in+%28production%2Cqa%29%2Ctier+in+%28frontend%29` * Evaluates to `environment in (production,qa),tier in (frontend)` Note: Ansible Galaxy and RHUI have agreed that for a first pass, we could just support a subset of operators (ie `=` and `!=`). ##### LabelSelectFilter `LabelSelectFilter` would be a `django_filter.Filter` that parses the label_select parameter and then filters the queryset. It can be applied to a Queryset of any model with labels. Note: for an example of a complex `Filter`, [see the `RepositoryVersionFilter`](https://git.io/JIJif). #### LabelSerializer Create a new `LabelSerializer` that can be nested into other model serializers as a field (much like the `CreatedResourceSerializer`). This serializer should be both readable and writable and should enable the following API calls. ##### Reading ``` # GET /pulp/api/v3/repositories/file/file/ { ... "labels": {"foo": "bar", "foo2": "baz"}, ... } ``` ##### Setting/Updating ``` # POST /pulp/api/v3/repositories/file/file/ name=test labels='[{"foo": "bar"}]' { ... "labels": {"foo": "bar"}, ... } # PUT /pulp/api/v3/repositories/file/file/<uuid>/ labels='[{"foo": "baz"}]' { ... "labels": {"foo": "baz"}, ... } ``` ### Database Design ##### Label (extends GenericRelationModel) * **resource** - generic foreign key * **key** (CharField) - the key of the label * **value** (TextField) - the value for a label Notes * `Label` `resource` resource and `key` key are unique together * We should also limit `key` key to alphanumerics * The `Label` gets deleted when the resource gets deleted (which `GenericRelationModel` GenericRelationModel does) * `key` Key and `value` value should be indexed (`db_index=True`)