API Usage with Ruby¶
This guide provides practical examples for using the Nextcloud Operator API with Ruby and the kubeclient gem.
Setup¶
Install the Kubernetes Ruby Client¶
Or add to your Gemfile:
Client Setup¶
require 'kubeclient'
require 'json'
# Load kubeconfig from default location
config = Kubeclient::Config.read(ENV['KUBECONFIG'] || File.expand_path('~/.kube/config'))
# Create client for core API
@client = Kubeclient::Client.new(
config.context.api_endpoint + '/api',
config.context.api_version,
ssl_options: config.context.ssl_options,
auth_options: config.context.auth_options
)
# Create client for custom resources (Nextcloud)
@custom_client = Kubeclient::Client.new(
config.context.api_endpoint + '/apis/k8s.bnerd.com',
'v1alpha1',
ssl_options: config.context.ssl_options,
auth_options: config.context.auth_options
)
Authentication¶
Using Service Account Token (Applications)¶
API_SERVER = ENV['KUBERNETES_SERVICE_HOST'] || 'https://kubernetes.default.svc'
TOKEN = File.read('/var/run/secrets/kubernetes.io/serviceaccount/token')
CA_CERT = '/var/run/secrets/kubernetes.io/serviceaccount/ca.crt'
ssl_options = { ca_file: CA_CERT, verify_ssl: OpenSSL::SSL::VERIFY_PEER }
auth_options = { bearer_token: TOKEN }
@client = Kubeclient::Client.new(
"#{API_SERVER}/api", 'v1',
ssl_options: ssl_options, auth_options: auth_options
)
@custom_client = Kubeclient::Client.new(
"#{API_SERVER}/apis/k8s.bnerd.com", 'v1alpha1',
ssl_options: ssl_options, auth_options: auth_options
)
Nextcloud CRUD Operations¶
Create from Pool¶
nextcloud = Kubeclient::Resource.new(
metadata: {
name: 'tenant-a-cloud',
namespace: 'tenant-a'
},
spec: {
poolSelector: { matchLabels: { profile: 'basic' } },
ingress: { host: 'nc-basic.example.com' }
}
)
begin
result = @custom_client.create_nextcloud(nextcloud)
puts "Created Nextcloud: #{result.metadata.name}"
rescue Kubeclient::ResourceAlreadyExistsError
puts "Nextcloud already exists"
rescue Kubeclient::HttpError => e
puts "Error: #{e.message}"
end
Create with Custom Configuration¶
nextcloud = Kubeclient::Resource.new(
metadata: { name: 'tenant-a-custom', namespace: 'tenant-a' },
spec: {
poolSelector: { matchLabels: { profile: 'basic' } },
version: '29',
ingress: { host: 'custom.tenant-a.example.com', tls: { enabled: true } },
database: { managed: true, type: 'postgresql' },
redis: { enabled: true }
}
)
result = @custom_client.create_nextcloud(nextcloud)
List All in Namespace¶
nextclouds = @custom_client.get_nextclouds(namespace: 'tenant-a')
nextclouds.each do |nc|
puts "#{nc.metadata.name} - Phase: #{nc.status&.phase} - URL: #{nc.status&.url}"
end
Get Specific Resource¶
begin
nc = @custom_client.get_nextcloud('tenant-a-cloud', 'tenant-a')
puts "Phase: #{nc.status&.phase}"
puts "URL: #{nc.status&.url}"
puts "Admin: #{nc.status&.admin&.username}"
rescue Kubeclient::ResourceNotFoundError
puts "Not found"
end
Get Admin Credentials¶
nc = @custom_client.get_nextcloud('tenant-a-cloud', 'tenant-a')
if nc.status&.admin
puts "URL: #{nc.status.url}"
puts "Username: #{nc.status.admin.username}"
puts "Password: #{nc.status.admin.password}"
end
Update¶
nc = @custom_client.get_nextcloud('tenant-a-cloud', 'tenant-a')
nc.spec.ingress.host = 'new-hostname.example.com'
@custom_client.update_nextcloud(nc)
Patch (Partial Update)¶
patch = { spec: { ingress: { host: 'patched.example.com' } } }
@custom_client.patch_nextcloud(
'tenant-a-cloud', patch, 'tenant-a', 'merge-patch'
)
Delete¶
begin
@custom_client.delete_nextcloud('tenant-a-cloud', 'tenant-a')
puts "Deleted"
rescue Kubeclient::ResourceNotFoundError
puts "Not found"
end
Status and Monitoring¶
Watch Resources¶
watcher = @custom_client.watch_nextclouds(namespace: 'tenant-a')
watcher.each do |notice|
nc = notice.object
case notice.type
when 'ADDED' then puts "ADDED: #{nc.metadata.name}"
when 'MODIFIED' then puts "MODIFIED: #{nc.metadata.name} (#{nc.status&.phase})"
when 'DELETED' then puts "DELETED: #{nc.metadata.name}"
end
end
Wait for Ready¶
def wait_for_ready(client, name, namespace, timeout: 300)
start_time = Time.now
loop do
nc = client.get_nextcloud(name, namespace)
phase = nc.status&.phase
return nc if phase == 'Ready'
elapsed = Time.now - start_time
raise "Timeout (phase: #{phase})" if elapsed > timeout
puts " Phase: #{phase} (#{elapsed.round}s)"
sleep 5
end
end
nc = wait_for_ready(@custom_client, 'tenant-a-cloud', 'tenant-a')
puts "URL: #{nc.status.url}"
Complete Example: Tenant Onboarding¶
#!/usr/bin/env ruby
require 'kubeclient'
require 'json'
class NextcloudTenantProvisioner
def initialize(config_file = nil)
config = Kubeclient::Config.read(
config_file || ENV['KUBECONFIG'] || File.expand_path('~/.kube/config')
)
@client = Kubeclient::Client.new(
config.context.api_endpoint + '/api', 'v1',
ssl_options: config.context.ssl_options,
auth_options: config.context.auth_options
)
@custom_client = Kubeclient::Client.new(
config.context.api_endpoint + '/apis/k8s.bnerd.com', 'v1alpha1',
ssl_options: config.context.ssl_options,
auth_options: config.context.auth_options
)
end
def provision_tenant(tenant_name:, nextcloud_name:, domain:, pool_profile: 'basic')
# Step 1: Create namespace
ns = Kubeclient::Resource.new(
metadata: { name: tenant_name, labels: { tenant: tenant_name } }
)
begin
@client.create_namespace(ns)
rescue Kubeclient::ResourceAlreadyExistsError
# OK
end
# Step 2: Create Nextcloud
nc = Kubeclient::Resource.new(
metadata: { name: nextcloud_name, namespace: tenant_name },
spec: {
poolSelector: { matchLabels: { profile: pool_profile } },
ingress: { host: domain, tls: { enabled: true } }
}
)
begin
@custom_client.create_nextcloud(nc)
rescue Kubeclient::ResourceAlreadyExistsError
# OK
end
# Step 3: Wait for ready
start_time = Time.now
loop do
result = @custom_client.get_nextcloud(nextcloud_name, tenant_name)
phase = result.status&.phase
if phase == 'Ready'
puts "Nextcloud is ready!"
puts " URL: #{result.status.url}"
puts " Admin: #{result.status.admin&.username}"
return result
end
raise "Timeout" if Time.now - start_time > 300
sleep 5
end
end
end
# Usage
if __FILE__ == $0
tenant = ARGV[0] || 'tenant-a'
name = ARGV[1] || 'main-cloud'
domain = ARGV[2] || "#{tenant}.example.com"
provisioner = NextcloudTenantProvisioner.new
provisioner.provision_tenant(
tenant_name: tenant,
nextcloud_name: name,
domain: domain
)
end
# Run with defaults
ruby provision_tenant.rb
# Run with custom values
ruby provision_tenant.rb tenant-b main-cloud tenant-b.example.com
Common HTTP Status Codes¶
| Code | Meaning | Ruby Exception |
|---|---|---|
| 200 | Success | - |
| 201 | Created | - |
| 404 | Not found | Kubeclient::ResourceNotFoundError |
| 409 | Already exists | Kubeclient::ResourceAlreadyExistsError |
| 422 | Invalid spec | Kubeclient::HttpError |
| 403 | Forbidden | Kubeclient::HttpError |
| 401 | Unauthorized | Kubeclient::HttpError |