Files
canine/lib/portainer/stack.rb
2025-11-25 21:16:06 -08:00

137 lines
3.9 KiB
Ruby

class Portainer::Stack
attr_reader :stack_manager, :client
delegate :authenticated?, to: :client
delegate :get_kubernetes_config, to: :client
def initialize(stack_manager)
@stack_manager = stack_manager
end
# Ugh, this is only used for testing.
def _connect_with_client(client)
@_client = client
self
end
def retrieve_access_token(user, allow_anonymous: false)
if !stack_manager.enable_role_based_access_control && stack_manager.access_token.present?
Portainer::Client::AccessToken.new(stack_manager.access_token)
elsif user.present? && user.portainer_jwt.present?
Portainer::Client::UserJWT.new(user.portainer_jwt)
elsif user.nil? && allow_anonymous && stack_manager.access_token.present?
Portainer::Client::AccessToken.new(stack_manager.access_token)
else
raise "No access token found for user or stack manager. Please check your configuration."
end
end
def connect(user, allow_anonymous: false)
@_client = Portainer::Client.new(
stack_manager.provider_url,
retrieve_access_token(user, allow_anonymous:),
)
self
end
def client
raise "Client not connected" unless @_client.present?
@_client
end
def requires_reauthentication?
stack_manager.access_token.blank?
end
def provides_authentication?
true
end
def provides_registries?
true
end
def provides_clusters?
true
end
def provides_logs?
true
end
def logs_url(service, pod_name)
service = service.name
container = service.project.name
namespace = service.project.namespace
cluster = service.project.cluster
"/#{cluster.external_id}/kubernetes/applications/#{namespace}/#{service}/#{pod_name}/#{container}/logs"
end
def sync_registries(
user,
target_cluster
)
kubectl = K8::Kubectl.new(K8::Connection.new(target_cluster, user))
response = client.registries
providers = response.map do |registry|
provider = user.providers.find_or_initialize_by(
external_id: registry.id,
)
provider.registry_url = registry.url
credentials = client.get_registry_secret(
provider.external_id,
target_cluster.external_id,
kubectl,
)
provider.auth = {
info: {
username: credentials['auths'][provider.registry_url]['username']
}
}.to_json
provider.access_token = credentials['auths'][provider.registry_url]['password']
provider.save!
provider
end
end
def sync_clusters
response = client.endpoints
response.map do |external_cluster|
cluster = stack_manager.account.clusters.find_or_initialize_by(external_id: external_cluster.id)
cluster.name = external_cluster.name
new_record = cluster.new_record?
cluster.save
if new_record
Clusters::InstallJob.perform_later(cluster, stack_manager.account.owner)
end
cluster
end
disappeared_clusters = stack_manager.account.clusters.select { |cluster| !response.map(&:id).map(&:to_s).include?(cluster.external_id.to_s) }
disappeared_clusters.each do |cluster|
cluster.deleted!
end
end
def fetch_kubeconfig(cluster)
full_kubeconfig = client.get_kubernetes_config
full_kubeconfig["clusters"] = full_kubeconfig["clusters"].select do |cluster_config|
cluster_config["cluster"]["server"].ends_with?("/api/endpoints/#{cluster.external_id}/kubernetes")
end
if full_kubeconfig["clusters"].empty?
raise "Cluster is not discoverable in the stack"
end
cluster_name = full_kubeconfig["clusters"][0]["name"]
full_kubeconfig["contexts"] = full_kubeconfig["contexts"].select do |context|
context["context"]["cluster"] == cluster_name
end
full_kubeconfig["current-context"] = full_kubeconfig["contexts"][0]["name"]
full_kubeconfig
end
def install_recipe
Clusters::Install::DEFAULT_RECIPE
end
end