Skip to content

Commit

Permalink
Merge pull request #541 from zendesk/rsim.delete_collection
Browse files Browse the repository at this point in the history
add delete_collection api
  • Loading branch information
cben authored Jun 9, 2022
2 parents 831e360 + 6b4232e commit 024f5df
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 0 deletions.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ watch_foos(name: 'name', **opts) # global single object
delete_foo('name', 'namespace', opts) # namespaced
delete_foo('name', nil, opts) # global
delete_foos(namespace: 'ns', **opts) # namespaced
create_foo(Kubeclient::Resource.new({metadata: {name: 'name', namespace: 'namespace', ...}, ...}))
create_foo(Kubeclient::Resource.new({metadata: {name: 'name', ...}, ...})) # global
Expand Down Expand Up @@ -684,6 +685,36 @@ delete_options = Kubeclient::Resource.new(
client.delete_deployment(deployment_name, namespace, delete_options: delete_options)
```

### Delete all instances of a specific entity type in a namespace
Such as: `delete_pods`, `delete_secrets`, `delete_services`, `delete_nodes`, `delete_replication_controllers`, `delete_resource_quotas`, `delete_limit_ranges`, `delete_persistent_volumes`, `delete_persistent_volume_claims`, `delete_component_statuses`, `delete_service_accounts`

Delete all entities of a specific type in a namespace, entities are returned in the same manner as for `get_services`:

```ruby
services = client.delete_services(namespace: 'development')
```

You can get entities which have specific labels by specifying a parameter named `label_selector` (named `labelSelector` in Kubernetes server):

```ruby
pods = client.delete_pods(namespace: 'development', label_selector: 'name=redis-master')
```

You can specify multiple labels (that option will return entities which have both labels:

```ruby
pods = client.delete_pods(namespace: 'default', label_selector: 'name=redis-master,app=redis')
```

There is also [a ability to filter by *some* fields](https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/). Which fields are supported is not documented, you can try and see if you get an error...
```ruby
client.delete_pods(namespace: 'development', field_selector: 'spec.nodeName=master-0')
```

With default (`as: :ros`) return format, the returned object acts like an array of the individual pods, but also supports a `.resourceVersion` method.

With `:parsed` and `:parsed_symbolized` formats, the returned data structure matches kubernetes list structure: it's a hash containing `metadata` and `items` keys, the latter containing the individual pods.

### Create an entity
For example: `create_pod pod_object`, `create_replication_controller rc_obj`, `create_secret secret_object`, `create_resource_quota resource_quota_object`, `create_limit_range limit_range_object`, `create_persistent_volume persistent_volume_object`, `create_persistent_volume_claim persistent_volume_claim_object`, `create_service_account service_account_object`

Expand Down
46 changes: 46 additions & 0 deletions lib/kubeclient.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ class Client
'continue' => :continue
}.freeze

DELETE_COLLECTION_ARGUMENTS = {
'continue' => :continue,
'dryRun' => :dry_run,
'fieldSelector' => :field_selector,
'gracePeriodSeconds' => :grace_period_seconds,
'labelSelector' => :label_selector,
'limit' => :limit,
'orphanDependents' => :orphan_dependents,
'propagationPolicy' => :propagation_policy,
'resourceVersion' => :resource_version,
'resourceVersionMatch' => :resource_version_match,
'timeoutSeconds' => :timeout_seconds
}.freeze

WATCH_ARGUMENTS = {
'labelSelector' => :label_selector,
'fieldSelector' => :field_selector,
Expand Down Expand Up @@ -294,6 +308,12 @@ def define_entity_methods
delete_entity(entity.resource_name, name, namespace, **opts)
end

# delete all entities of a type e.g. delete_pods, etc.
define_singleton_method("delete_#{entity.method_names[1]}") \
do |options = {}|
delete_collection(entity.entity_type, entity.resource_name, options)
end

define_singleton_method("create_#{entity.method_names[0]}") do |entity_config|
create_entity(entity.entity_type, entity.resource_name, entity_config)
end
Expand Down Expand Up @@ -453,6 +473,32 @@ def delete_entity(resource_name, name, namespace = nil, delete_options: {})
format_response(@as, response.body)
end

# Accepts the following options:
# :namespace (string) - the namespace of the entity.
# :label_selector (string) - a selector to restrict the list of returned objects by labels.
# :field_selector (string) - a selector to restrict the list of returned objects by fields.
# :grace_period_seconds (integer) - the duration before an object should be deleted
# :limit (integer) - a maximum number of items to return in each response
# :orphan_dependents (bool) - should the dependent objects be orphaned
# :propagation_policy (string) - one of Foreground|Background|Orphan
# :resource_version (string) - sets a limit on the resource versions that can be served
# :resource_version_match (string) - determines how the resource_version constraint will be applied
# :timeout_seconds (integer) - limits the duraiton of the call
# :continue (string) - a token used to retrieve the next chunk of entities
# :as (:raw|:ros) - defaults to :ros
# :raw - return the raw response body as a string
# :ros - return a collection of RecursiveOpenStruct objects
def delete_collection(entity_type, resource_name, options = {})
params = {}
DELETE_COLLECTION_ARGUMENTS.each { |k, v| params[k] = options[v] if options[v] }

ns_prefix = build_namespace_prefix(options[:namespace])
response = handle_exception do
faraday_client.delete("#{ns_prefix}#{resource_name}", params)
end
format_response(options[:as] || @as, response.body, entity_type)
end

def create_entity(entity_type, resource_name, entity_config)
# Duplicate the entity_config to a hash so that when we assign
# kind and apiVersion, this does not mutate original entity_config obj.
Expand Down
53 changes: 53 additions & 0 deletions test/test_kubeclient.rb
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,54 @@ def test_entity_list_parsed_symbolized
assert_equal(%i[metadata spec status], response[:items].first.keys)
end

def test_delete_collection
stub_core_api_list
stub_delete_pods

pods = client.delete_pods(namespace: 'foo')

refute_empty(pods)
assert_instance_of(Kubeclient::Common::EntityList, pods)
# Stripping of 'List' in collection.kind RecursiveOpenStruct mode only is historic.
assert_equal('Pod', pods.kind)
assert_equal(1, pods.size)
assert_instance_of(Kubeclient::Resource, pods[0])

assert_requested(:delete, 'http://localhost:8080/api/v1/namespaces/foo/pods', times: 1)
end

def test_delete_collection_raw
stub_core_api_list
stub_delete_pods

response = client.delete_pods(namespace: 'foo', as: :raw)

refute_empty(response)
assert_equal(open_test_file('pod_list.json').read, response)

assert_requested(:delete, 'http://localhost:8080/api/v1/namespaces/foo/pods', times: 1)
end

def test_delete_collection_parsed
stub_core_api_list
stub_delete_pods

response = client.delete_pods(namespace: 'foo', as: :parsed)
assert_equal(Hash, response.class)
assert_equal('PodList', response['kind'])
assert_equal(%w[metadata spec status], response['items'].first.keys)
end

def test_delete_collection_parsed_symbolized
stub_core_api_list
stub_delete_pods

response = client.delete_pods(namespace: 'foo', as: :parsed_symbolized)
assert_equal(Hash, response.class)
assert_equal('PodList', response[:kind])
assert_equal(%i[metadata spec status], response[:items].first.keys)
end

def test_custom_faraday_config_options
client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
expected_middlewares = [FaradayMiddleware::FollowRedirects, Faraday::Response::RaiseError]
Expand Down Expand Up @@ -926,6 +974,11 @@ def stub_get_services
.to_return(body: open_test_file('entity_list.json'), status: 200)
end

def stub_delete_pods
stub_request(:delete, %r{/pods})
.to_return(body: open_test_file('pod_list.json'), status: 200)
end

def client
@client ||= Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
end
Expand Down

0 comments on commit 024f5df

Please sign in to comment.