From 838233ad91868c05e4ec7a5dfbc4ef6e5f074ca2 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Sun, 5 Sep 2021 21:23:26 +0300 Subject: [PATCH 1/3] Upgraded PHP K8s --- composer.json | 2 +- src/KubernetesCluster.php | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/composer.json b/composer.json index 1e084b7..e2bcd05 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ } ], "require": { - "renoki-co/php-k8s": "^2.2" + "renoki-co/php-k8s": "^3.0" }, "autoload": { "psr-4": { diff --git a/src/KubernetesCluster.php b/src/KubernetesCluster.php index 20927fa..83f4059 100644 --- a/src/KubernetesCluster.php +++ b/src/KubernetesCluster.php @@ -4,6 +4,9 @@ use RenokiCo\PhpK8s\KubernetesCluster as PhpK8sCluster; +/** + * @see \RenokiCo\PhpK8s\KubernetesCluster + */ class KubernetesCluster { /** @@ -28,7 +31,7 @@ public function __construct(array $config) * Switch the connection. * * @param string $connection - * @return $this + * @return \RenokiCo\LaravelK8s\KubernetesCluster */ public function connection(string $connection) { @@ -47,8 +50,6 @@ public function connection(string $connection) */ protected function loadFromConfig(array $config) { - $this->cluster = new PhpK8sCluster('http://127.0.0.1:8080'); - switch ($config['driver'] ?? null) { case 'kubeconfig': $this->configureWithKubeConfigFile($config); break; case 'http': $this->configureWithHttpAuth($config); break; @@ -66,7 +67,7 @@ protected function loadFromConfig(array $config) */ protected function configureWithKubeConfigFile(array $config) { - $this->cluster->fromKubeConfigYamlFile( + $this->cluster = PhpK8sCluster::fromKubeConfigYamlFile( $config['path'], $config['context'] ); } @@ -79,7 +80,7 @@ protected function configureWithKubeConfigFile(array $config) */ protected function configureWithHttpAuth(array $config) { - $this->cluster = new PhpK8sCluster($config['host']); + $this->cluster = PhpK8sCluster::fromUrl($config['host']); if ($config['ssl']['verify'] ?? true) { $this->cluster->withCertificate( @@ -111,7 +112,7 @@ protected function configureWithHttpAuth(array $config) */ protected function configureWithToken(array $config) { - $this->cluster = new PhpK8sCluster($config['host']); + $this->cluster = PhpK8sCluster::fromUrl($config['host']); if ($config['ssl']['verify'] ?? true) { $this->cluster->withCertificate( @@ -140,9 +141,7 @@ protected function configureWithToken(array $config) */ protected function configureInCluster(array $config) { - $this->cluster = new PhpK8sCluster($config['host'] ?? 'https://kubernetes.default.svc.cluster.local'); - - $this->cluster->inClusterConfiguration(); + $this->cluster = PhpK8sCluster::inClusterConfiguration(); } /** From 06a49b20303013f66b0fd4dd209087e68c7e2148 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Sun, 5 Sep 2021 21:25:08 +0300 Subject: [PATCH 2/3] Added static method annotations --- src/LaravelK8sFacade.php | 99 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/src/LaravelK8sFacade.php b/src/LaravelK8sFacade.php index f62029c..7f86fd1 100644 --- a/src/LaravelK8sFacade.php +++ b/src/LaravelK8sFacade.php @@ -4,6 +4,105 @@ use Illuminate\Support\Facades\Facade; +/** + * @method static \RenokiCo\LaravelK8s\KubernetesCluster connection(string $connection) + * @method static \RenokiCo\PhpK8s\KubernetesCluster getCluster() + * @method static \RenokiCo\PhpK8s\Kinds\K8sNode node(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sNode getNodeByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllNodes(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sEvent event(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sEvent getEventByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllEventsFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllEvents(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sNamespace namespace(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sNamespace getNamespaceByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllNamespaces(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sConfigMap configmap(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sConfigMap getConfigmapByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllConfigmapsFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllConfigmaps(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sSecret secret(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sSecret getSecretByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllSecretsFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllSecrets(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sIngress ingress(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sIngress getIngressByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllIngressesFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllIngresses(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sService service(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sService getServiceByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllServicesFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllServices(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sStorageClass storageClass(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sStorageClass getStorageClassByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllStorageClassesFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllStorageClasses(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sPersistentVolume persistentVolume(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sPersistentVolume getPersistentVolumeByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllPersistentVolumesFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllPersistentVolumes(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sPersistentVolumeClaim persistentVolumeClaim(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sPersistentVolumeClaim getPersistentVolumeClaimByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllPersistentVolumeClaimsFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllPersistentVolumeClaims(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sPod pod(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sPod getPodByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllPodsFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllPods(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sStatefulSet statefulSet(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sStatefulSet getStatefulSetByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllStatefulSetsFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllStatefulSets(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sDeployment deployment(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sDeployment getDeploymentByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllDeploymentsFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllDeployments(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sJob job(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sJob getJobByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllJobsFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllJobs(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sCronJob cronjob(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sCronJob getCronjobByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllCronjobsFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllCronjobs(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sDaemonSet daemonSet(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sDaemonSet getDaemonSetByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllDaemonSetsFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllDaemonSets(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sHorizontalPodAutoscaler horizontalPodAutoscaler(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sHorizontalPodAutoscaler getHorizontalPodAutoscalerByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllHorizontalPodAutoscalersFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllHorizontalPodAutoscalers(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sServiceAccount serviceAccount(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sServiceAccount getServiceAccountByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllServiceAccountsFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllServiceAccounts(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sRole role(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sRole getRoleByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllRolesFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllRoles(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sClusterRole clusterRole(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sClusterRole getClusterRoleByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllClusterRolesFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllClusterRoles(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sRoleBinding roleBinding(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sRoleBinding getRoleBindingByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllRoleBindingsFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllRoleBindings(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sClusterRoleBinding clusterRoleBinding(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sClusterRoleBinding getClusterRoleBindingByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllClusterRoleBindingsFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllClusterRoleBindings(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sPodDisruptionBudget podDisruptionBudget(array $attributes = []) + * @method static \RenokiCo\PhpK8s\Kinds\K8sPodDisruptionBudget getPodDisruptionBudgetByName(string $name, string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllPodDisruptionBudgetsFromAllNamespaces(array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\ResourcesList getAllPodDisruptionBudgets(string $namespace = 'default', array $query = ['pretty' => 1]) + * @method static \RenokiCo\PhpK8s\Kinds\K8sResource|array[\RenokiCo\PhpK8s\Kinds\K8sResource] fromYaml(string $yaml) + * @method static \RenokiCo\PhpK8s\Kinds\K8sResource|array[\RenokiCo\PhpK8s\Kinds\K8sResource] fromYamlFile(string $path, \Closure $callback = null) + * @method static \RenokiCo\PhpK8s\Kinds\K8sResource|array[\RenokiCo\PhpK8s\Kinds\K8sResource] fromTemplatedYamlFile(string $path, array $replace, \Closure $callback = null) + * + * @see \RenokiCo\LaravelK8s\KubernetesCluster + */ class LaravelK8sFacade extends Facade { /** From 2dc919fbe5ddaf1a3f855c2e6469730b45ff02d8 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Sun, 5 Sep 2021 21:49:52 +0300 Subject: [PATCH 3/3] Added environment variable provider --- config/k8s.php | 20 ++++++++++++++++++++ src/KubernetesCluster.php | 13 +++++++++++++ tests/ConfigurationTest.php | 25 +++++++++++++++++++++++++ tests/TestCase.php | 10 ++++++++++ tests/cluster/kubeconfig-2.yaml | 10 ++++++++++ 5 files changed, 78 insertions(+) create mode 100644 tests/cluster/kubeconfig-2.yaml diff --git a/config/k8s.php b/config/k8s.php index ca7e8b8..b15ede1 100644 --- a/config/k8s.php +++ b/config/k8s.php @@ -96,6 +96,26 @@ 'host' => env('KUBE_HOST', 'https://kubernetes.default.svc.cluster.local'), ], + /* + |-------------------------------------------------------------------------- + | Environment Variable Driver + |-------------------------------------------------------------------------- + | + | The environment variable driver leverages your current (possibly set) + | KUBECONFIG environment variable. The variable contains a list of paths + | towards multiple kubeconfig files that will be read, merged and based + | on the selected context from the configuration, it will connect + | to the cluster, just like the "kubeconfig" driver. + | + | Read more: https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/ + | + */ + + 'environment' => [ + 'driver' => 'environment', + 'context' => env('KUBECONFIG_CONTEXT', 'minikube'), + ], + ], ]; diff --git a/src/KubernetesCluster.php b/src/KubernetesCluster.php index 83f4059..6584624 100644 --- a/src/KubernetesCluster.php +++ b/src/KubernetesCluster.php @@ -55,6 +55,7 @@ protected function loadFromConfig(array $config) case 'http': $this->configureWithHttpAuth($config); break; case 'token': $this->configureWithToken($config); break; case 'cluster': $this->configureInCluster($config); break; + case 'variable': $this->configureWithKubeConfigVariable($config); break; default: break; } } @@ -144,6 +145,18 @@ protected function configureInCluster(array $config) $this->cluster = PhpK8sCluster::inClusterConfiguration(); } + /** + * Configure the cluster using the + * KUBECONFIG environment variable. + * + * @param array $config + * @return void + */ + protected function configureWithKubeConfigVariable(array $config) + { + $this->cluster = PhpK8sCluster::fromKubeConfigVariable($config['context']); + } + /** * Get the initialized cluster. * diff --git a/tests/ConfigurationTest.php b/tests/ConfigurationTest.php index 9a75295..4fe7878 100644 --- a/tests/ConfigurationTest.php +++ b/tests/ConfigurationTest.php @@ -130,4 +130,29 @@ public function test_in_cluster_config() $this->assertEquals('/var/run/secrets/kubernetes.io/serviceaccount/ca.crt', $caPath); $this->assertEquals('some-namespace', K8sResource::$defaultNamespace); } + + /** + * @dataProvider environmentVariableContextProvider + */ + public function test_from_environment_variable(string $context = null, string $expectedDomain) + { + $_SERVER['KUBECONFIG'] = __DIR__.'/cluster/kubeconfig.yaml::'.__DIR__.'/cluster/kubeconfig-2.yaml'; + + $this->app['config']->set('k8s.default', 'variable'); + $this->app['config']->set('k8s.connections.variable', [ + 'driver' => 'variable', + 'context' => $context, + ]); + + $cluster = LaravelK8sFacade::connection('variable')->getCluster(); + + $this->assertSame("https://{$expectedDomain}:8443/?", $cluster->getCallableUrl('/', [])); + } + + public function environmentVariableContextProvider(): iterable + { + yield [null, 'minikube']; + yield ['minikube-2', 'minikube-2']; + yield ['minikube-3', 'minikube-3']; + } } diff --git a/tests/TestCase.php b/tests/TestCase.php index adca6d3..9cf2cbf 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -6,6 +6,16 @@ abstract class TestCase extends Orchestra { + /** + * {@inheritDoc} + */ + public function tearDown(): void + { + parent::tearDown(); + + unset($_SERVER['KUBECONFIG']); + } + /** * {@inheritdoc} */ diff --git a/tests/cluster/kubeconfig-2.yaml b/tests/cluster/kubeconfig-2.yaml new file mode 100644 index 0000000..a3f2a77 --- /dev/null +++ b/tests/cluster/kubeconfig-2.yaml @@ -0,0 +1,10 @@ +clusters: + - cluster: + certificate-authority-data: c29tZS1jYQo= # "some-ca" + server: https://minikube-3:8443 + name: minikube-3 +contexts: + - context: + cluster: minikube-3 + user: minikube + name: minikube-3