src/KubernetesCluster.php
<?php
namespace RenokiCo\PhpK8s;
use Closure;
use Illuminate\Support\Str;
use RenokiCo\PhpK8s\Exceptions\KubernetesAPIException;
use RenokiCo\PhpK8s\Kinds\K8sResource;
/**
* @method \RenokiCo\PhpK8s\Kinds\K8sNode node(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sNode getNodeByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllNodes(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sEvent event(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sEvent getEventByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllEventsFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllEvents(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sNamespace namespace(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sNamespace getNamespaceByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllNamespaces(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sConfigMap configmap(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sConfigMap getConfigmapByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllConfigmapsFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllConfigmaps(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sSecret secret(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sSecret getSecretByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllSecretsFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllSecrets(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sIngress ingress(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sIngress getIngressByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllIngressesFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllIngresses(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sService service(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sService getServiceByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllServicesFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllServices(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sStorageClass storageClass(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sStorageClass getStorageClassByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllStorageClassesFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllStorageClasses(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sPersistentVolume persistentVolume(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sPersistentVolume getPersistentVolumeByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllPersistentVolumesFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllPersistentVolumes(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sPersistentVolumeClaim persistentVolumeClaim(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sPersistentVolumeClaim getPersistentVolumeClaimByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllPersistentVolumeClaimsFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllPersistentVolumeClaims(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sPod pod(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sPod getPodByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllPodsFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllPods(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sStatefulSet statefulSet(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sStatefulSet getStatefulSetByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllStatefulSetsFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllStatefulSets(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sDeployment deployment(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sDeployment getDeploymentByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllDeploymentsFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllDeployments(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sJob job(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sJob getJobByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllJobsFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllJobs(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sCronJob cronjob(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sCronJob getCronjobByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllCronjobsFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllCronjobs(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sDaemonSet daemonSet(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sDaemonSet getDaemonSetByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllDaemonSetsFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllDaemonSets(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sHorizontalPodAutoscaler horizontalPodAutoscaler(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sHorizontalPodAutoscaler getHorizontalPodAutoscalerByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllHorizontalPodAutoscalersFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllHorizontalPodAutoscalers(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sServiceAccount serviceAccount(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sServiceAccount getServiceAccountByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllServiceAccountsFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllServiceAccounts(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sRole role(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sRole getRoleByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllRolesFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllRoles(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sClusterRole clusterRole(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sClusterRole getClusterRoleByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllClusterRolesFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllClusterRoles(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sRoleBinding roleBinding(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sRoleBinding getRoleBindingByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllRoleBindingsFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllRoleBindings(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sClusterRoleBinding clusterRoleBinding(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sClusterRoleBinding getClusterRoleBindingByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllClusterRoleBindingsFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllClusterRoleBindings(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sPodDisruptionBudget podDisruptionBudget(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sPodDisruptionBudget getPodDisruptionBudgetByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllPodDisruptionBudgetsFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllPodDisruptionBudgets(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sValidatingWebhookConfiguration validatingWebhookConfiguration(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sValidatingWebhookConfiguration getValidatingWebhookConfigurationByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllValidatingWebhookConfigurationsFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllValidatingWebhookConfiguration(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sMutatingWebhookConfiguration mutatingWebhookConfiguration(array $attributes = [])
* @method \RenokiCo\PhpK8s\Kinds\K8sMutatingWebhookConfiguration getMutatingWebhookConfigurationByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllMutatingWebhookConfigurationsFromAllNamespaces(array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\ResourcesList getAllMutatingWebhookConfiguration(string $namespace = 'default', array $query = ['pretty' => 1])
* @method \RenokiCo\PhpK8s\Kinds\K8sResource|array[\RenokiCo\PhpK8s\Kinds\K8sResource] fromYaml(string $yaml)
* @method \RenokiCo\PhpK8s\Kinds\K8sResource|array[\RenokiCo\PhpK8s\Kinds\K8sResource] fromYamlFile(string $path, \Closure $callback = null)
* @method \RenokiCo\PhpK8s\Kinds\K8sResource|array[\RenokiCo\PhpK8s\Kinds\K8sResource] fromTemplatedYamlFile(string $path, array $replace, \Closure $callback = null)
* @method static void registerCrd(string $class, string $name = null)
*
* @see \RenokiCo\PhpK8s\K8s
*/
class KubernetesCluster
{
use Traits\Cluster\AuthenticatesCluster;
use Traits\Cluster\ChecksClusterVersion;
use Traits\Cluster\LoadsFromKubeConfig;
use Traits\Cluster\MakesHttpCalls;
use Traits\Cluster\MakesWebsocketCalls;
/**
* The Cluster API port.
*
* @var string
*/
protected $url;
/**
* The class name for the K8s resource.
*
* @var string
*/
protected $resourceClass;
/**
* List all named operations with
* their respective methods for the
* HTTP request.
*
* @var array
*/
protected static $operations = [
self::GET_OP => 'GET',
self::CREATE_OP => 'POST',
self::REPLACE_OP => 'PUT',
self::DELETE_OP => 'DELETE',
self::LOG_OP => 'GET',
self::WATCH_OP => 'GET',
self::WATCH_LOGS_OP => 'GET',
self::EXEC_OP => 'POST',
self::ATTACH_OP => 'POST',
];
const GET_OP = 'get';
const CREATE_OP = 'create';
const REPLACE_OP = 'replace';
const DELETE_OP = 'delete';
const LOG_OP = 'logs';
const WATCH_OP = 'watch';
const WATCH_LOGS_OP = 'watch_logs';
const EXEC_OP = 'exec';
const ATTACH_OP = 'attach';
/**
* Create a new class instance.
*
* @param string|null $url
* @return void
*/
public function __construct(string $url = null)
{
$this->url = $url;
}
/**
* Set the K8s resource class.
*
* @param string $resourceClass
* @return $this
*/
public function setResourceClass(string $resourceClass)
{
$this->resourceClass = $resourceClass;
return $this;
}
/**
* Run a specific operation for the API path with a specific payload.
*
* @param string $operation
* @param string $path
* @param string|null|Closure $payload
* @param array $query
* @return mixed
*
* @throws \RenokiCo\PhpK8s\Exceptions\KubernetesAPIException
*/
public function runOperation(string $operation, string $path, $payload = '', array $query = ['pretty' => 1])
{
switch ($operation) {
case static::WATCH_OP: return $this->watchPath($path, $payload, $query);
break;
case static::WATCH_LOGS_OP: return $this->watchLogsPath($path, $payload, $query);
break;
case static::EXEC_OP: return $this->execPath($path, $query);
break;
case static::ATTACH_OP: return $this->attachPath($path, $payload, $query);
break;
default: break;
}
$method = static::$operations[$operation] ?? static::$operations[static::GET_OP];
return $this->makeRequest($method, $path, $payload, $query);
}
/**
* Watch for the current resource or a resource list.
*
* @param string $path
* @param Closure $callback
* @param array $query
* @return bool
*/
protected function watchPath(string $path, Closure $callback, array $query = ['pretty' => 1])
{
$resourceClass = $this->resourceClass;
$sock = $this->createSocketConnection($this->getCallableUrl($path, $query));
$data = null;
while (($data = fgets($sock)) == true) {
$data = @json_decode($data, true);
['type' => $type, 'object' => $attributes] = $data;
$call = call_user_func(
$callback,
$type,
new $resourceClass($this, $attributes)
);
if (! is_null($call)) {
fclose($sock);
unset($data);
return $call;
}
}
}
/**
* Watch for the logs for the resource.
*
* @param string $path
* @param Closure $callback
* @param array $query
* @return bool
*/
protected function watchLogsPath(string $path, Closure $callback, array $query = ['pretty' => 1])
{
$sock = $this->createSocketConnection($this->getCallableUrl($path, $query));
$data = null;
while (($data = fgets($sock)) == true) {
$call = call_user_func($callback, $data);
if (! is_null($call)) {
fclose($sock);
unset($data);
return $call;
}
}
}
/**
* Call exec on the resource.
*
* @param string $path
* @param array $query
* @return mixed
*
* @throws \RenokiCo\PhpK8s\Exceptions\KubernetesAPIException
*/
protected function execPath(
string $path,
array $query = ['pretty' => 1, 'stdin' => 1, 'stdout' => 1, 'stderr' => 1, 'tty' => 1]
) {
try {
return $this->makeRequest(static::$operations[static::EXEC_OP], $path, '', $query);
} catch (KubernetesAPIException $e) {
$payload = $e->getPayload();
// Check of the request needs upgrade and make a call to WS if needed.
if (
$payload['code'] === 400 &&
$payload['status'] === 'Failure' &&
$payload['message'] === 'Upgrade request required'
) {
return $this->makeWsRequest($path, null, $query);
}
throw $e;
}
}
/**
* Call attach on the resource.
*
* @param string $path
* @param Closure $callback
* @param array $query
* @return mixed
*
* @throws \RenokiCo\PhpK8s\Exceptions\KubernetesAPIException
*/
protected function attachPath(
string $path,
Closure $callback,
array $query = ['pretty' => 1, 'stdin' => 1, 'stdout' => 1, 'stderr' => 1, 'tty' => 1]
) {
try {
return $this->makeRequest(static::$operations[static::ATTACH_OP], $path, '', $query);
} catch (KubernetesAPIException $e) {
$payload = $e->getPayload();
// Check of the request needs upgrade and make a call to WS if needed.
if (
$payload['code'] === 400 &&
$payload['status'] === 'Failure' &&
$payload['message'] === 'Upgrade request required'
) {
return $this->makeWsRequest($path, $callback, $query);
}
throw $e;
}
}
/**
* Proxy the custom method to the K8s class.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
// Proxy the ->get[Resource]ByName($name, $namespace = 'default')
// For example, ->getConfigMapByName('settings')
if (preg_match('/get(.+)ByName/', $method, $matches)) {
[$method, $resource] = $matches;
// Check the method from the proxied K8s::class exists.
// For example, the method ->configmap() should exist.
if (method_exists(K8s::class, $resource)) {
return $this->{$resource}()
->whereNamespace($parameters[1] ?? K8sResource::$defaultNamespace)
->getByName($parameters[0], $parameters[2] ?? ['pretty' => 1]);
}
}
// Proxy the ->getAll[Resources]FromAllNamespaces($query = [...])
// For example, ->getAllIngressesFromAllNamespaces()
if (preg_match('/getAll(.+)FromAllNamespaces/', $method, $matches)) {
[$method, $resourcePlural] = $matches;
$resource = Str::singular($resourcePlural);
if (method_exists(K8s::class, $resource)) {
return $this->{$resource}()->allNamespaces($parameters[0] ?? ['pretty' => 1]);
}
}
// Proxy the ->getAll[Resources]($namespace = 'default', $query = [...])
// For example, ->getAllServices('staging')
if (preg_match('/getAll(.+)/', $method, $matches)) {
[$method, $resourcePlural] = $matches;
$resource = Str::singular($resourcePlural);
if (method_exists(K8s::class, $resource)) {
return $this->{$resource}()
->whereNamespace($parameters[0] ?? K8sResource::$defaultNamespace)
->all($parameters[1] ?? ['pretty' => 1]);
}
}
return K8s::{$method}($this, ...$parameters);
}
}