Уніфікований доступ до сервера API за допомогою clientcmd
Якщо ви коли-небудь хотіли створювати консольного клієнта для API Kubernetes, особливо якщо ви розглядали можливість використання вашого клієнта як втулка kubectl, ви, можливо, замислювалися над тим, як зробити вашого клієнта звичним для користувачів kubectl. Швидкий погляд на вихідні дані kubectl options може вас збентежити: «Невже я повинен реалізувати всі ці опції?»
Не бійтеся, інші вже зробили за вас велику частину роботи. Насправді проєкт Kubernetes надає дві бібліотеки, які допоможуть вам обробляти аргументи командного рядка в стилі kubectl у програмах Go: clientcmd та cli-runtime (яка використовує clientcmd). У цій статті ми покажемо, як використовувати першу з них.
Загальна філософія
Як і слід було очікувати, оскільки це частина client-go, кінцевою метою clientcmd є надання екземпляра restclient.Config, який може надсилати запити до сервера API.
Він дотримується семантики kubectl:
- стандартні значення беруться з
~/.kubeабо еквівалентного файлу; - файли можна вказати за допомогою змінної середовища
KUBECONFIG; - усі вищезазначені налаштування можна додатково замінити за допомогою аргументів командного рядка.
Він не встановлює аргумент командного рядка --kubeconfig, що може знадобитися для узгодження з kubectl; як це зробити, ви дізнаєтеся в розділі «Привʼязування прапорців».
Доступні функції
clientcmd дозволяє програмам обробляти
- вибір
kubeconfig(за допомогоюKUBECONFIG); - вибір контексту;
- вибір простору імен;
- сертифікати клієнта та приватні ключі;
- імітацію користувача;
- підтримку базової автентифікації HTTP (імʼя користувача/пароль).
Обʼєднання конфігурацій
У різних сценаріях clientcmd підтримує обʼєднання параметрів конфігурацій: KUBECONFIG може вказувати декілька файлів, вміст яких обʼєднується. Це може викликати плутанину, оскільки параметри обʼєднуються в різних напрямках залежно від того, як вони реалізовані. Якщо параметр визначений у мапі, перше визначення має пріоритет, а наступні визначення ігноруються. Якщо параметр не визначений у мапі, останнє визначення має пріоритет.
Коли налаштування отримуються за допомогою KUBECONFIG, відсутні файли призводять лише до попереджень. Якщо користувач явно вказує шлях (у стилі --kubeconfig), повинен бути відповідний файл.
Якщо KUBECONFIG не визначено, замість нього використовується стандартний файл конфігурації ~/.kube/config, якщо він є.
Загальний процес
Загальна схема використання стисло викладена в документації до пакунка clientcmd:
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
// якщо ви хочете змінити правила завантаження (які файли в якому порядку), ви можете зробити це тут
configOverrides := &clientcmd.ConfigOverrides{}
// якщо ви хочете змінити значення перевизначення або привʼязати їх до прапорців, є методи, які допоможуть вам
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
config, err := kubeConfig.ClientConfig()
if err != nil {
//Щось робимо
}
client, err := metav1.New(config)
// ...
У контексті цієї статті є шість кроків:
- Налаштування правил завантаження.
- Налаштування перевизначення.
- Створення набору прапорців.
- Привʼязування прапорів.
- Створення обʼєднаної конфігурації.
- Отримання клієнта API.
Налаштування правил завантаження
clientcmd.NewDefaultClientConfigLoadingRules() створює правила завантаження, які будуть використовувати або вміст змінної середовища KUBECONFIG, або імʼя стандартного файлу конфігурації (~/.kube/config). Крім того, якщо використовується стандартний файл конфігурації, він може перенести налаштування з (дуже) старого стандартного файлу конфігурації (~/.kube/.kubeconfig).
Ви можете створити власні ClientConfigLoadingRules, але в більшості випадків стандартні налаштування підходять.
Налаштування перевизначення
clientcmd.ConfigOverrides — це структура, що зберігає перевизначення, які будуть застосовані до налаштувань, завантажених із конфігурації, отриманої за допомогою правил завантаження. У контексті цієї статті її основною метою є зберігання значень, отриманих з аргументів командного рядка. Вони обробляються за допомогою бібліотеки pflag, яка є повноцінною заміною пакунка Go flag і додає підтримку аргументів з подвійним дефісом і довгими іменами.
У більшості випадків в перевизначеннях нічого не потрібно встановлювати; я лише привʼяжу їх до прапорців.
Створення набору прапорців
У цьому контексті прапорець є уособленням аргументу командного рядка, що вказує його повну назву (наприклад, --namespace), його скорочену назву, якщо така є (наприклад, -n), його стандартне значення та опис, що показується в інформації про використання. Прапорці зберігаються в екземплярах структури FlagInfo.
Доступні три набори прапорців, що представляють такі аргументи командного рядка:
- аргументи автентифікації (сертифікати, токени, імітації іншого користувача, імʼя користувача/пароль);
- аргументи кластера (сервер API, центр сертифікації, конфігурація TLS, проксі, стиснення)
- аргументи контексту (ім'я кластера, імʼя користувача
kubeconfig, простір імен)
Рекомендований вибір включає всі три з аргументом вибору іменованого контексту та аргументом часу очікування.
Всі вони доступні за допомогою функцій Recommended…Flags. Функції приймають префікс, який додається до всіх довгих імен аргументів.
Отже, виклик clientcmd.RecommendedConfigOverrideFlags(«») призводить до появи аргументів командного рядка, таких як --context, --namespace тощо. Аргументу --timeout присвоюється стандартне значення 0, а аргумент --namespace має відповідний короткий варіант -n. Додавання префікса, наприклад "from-", призводить до появи аргументів командного рядка, таких як --from-context, --from-namespace тощо. Це може здатися не особливо корисним для команд, що стосуються одного сервера API, але вони стають у пригоді, коли залучено кілька серверів API, наприклад у сценаріях із кількома кластерами.
Тут є потенційна проблема: префікси не змінюють коротке імʼя, тому --namespace потребує певної обережності, якщо використовується кілька префіксів: лише один із префіксів може бути повʼязаний із коротким імʼям -n. Вам доведеться очистити короткі імена, повʼязані з іншими префіксами --namespace, або, можливо, всі префікси, якщо немає логічного звʼязку з -n. Короткі імена можна очистити таким чином:
kflags := clientcmd.RecommendedConfigOverrideFlags(prefix)
kflags.ContextOverrideFlags.Namespace.ShortName = ""
Аналогічним чином прапорці можна повністю вимкнути, очистивши їх довге імʼя:
kflags.ContextOverrideFlags.Namespace.LongName = ""
Привʼязування прапорів
Після визначення набору прапорців його можна використовувати для привʼязки аргументів командного рядка до перевизначення за допомогою clientcmd.BindOverrideFlags. Для цього потрібен pflag FlagSet, а не пакунок flag з Go.
Якщо ви також хочете привʼязати --kubeconfig, зробіть це зараз, привʼязавши ExplicitPath у правилах завантаження:
flags.StringVarP(&loadingRules.ExplicitPath, "kubeconfig", "", "", "absolute path(s) to the kubeconfig file(s)")
Створення обʼєднаної конфігурації
Для створення обʼєднаної конфігурації доступні дві функції:
clientcmd.NewInteractiveDeferredLoadingClientConfigclientcmd.NewNonInteractiveDeferredLoadingClientConfig
Як випливає з назв, різниця між цими двома функціями полягає в тому, що перша може запитувати інформацію для автентифікації в інтерактивному режимі, використовуючи наданий зчитувач, тоді як друга працює тільки з інформацією, наданою їй викликом.
Слово "deferred" (відкладене) у назвах цих функцій означає, що остаточна конфігурація буде визначена якомога пізніше. Це означає, що ці функції можна викликати до аналізу аргументів командного рядка, а в результаті конфігурація буде використовувати будь-які значення, проаналізовані на момент її фактичного створення.
Отримання клієнта API
Обʼєднана конфігурація повертається як екземпляр ClientConfig. API-клієнт можна отримати з нього, викликавши метод ClientConfig().
Якщо конфігурація не вказана (KUBECONFIG порожній або вказує на відсутні файли, ~/.kube/config не існує, а конфігурація не вказана за допомогою аргументів командного рядка), стандартна настройка поверне незрозумілу помилку, що посилається на KUBERNETES_MASTER. Це застаріла поведінка; було зроблено кілька спроб позбутися її, але вона збереглася для аргументів командного рядка --local та --dry-run у --kubectl. Ви повинні перевіряти наявність помилок «порожньої конфігурації», викликаючи clientcmd.IsEmptyConfig(), і надавати чіткіше повідомлення про помилку.
Метод Namespace() також корисний: він повертає простір імен, який слід використовувати. Він також вказує, чи був простір імен перезаписаний користувачем (за допомогою --namespace).
Повний приклад
Ось повний приклад.
package main
import (
"context"
"fmt"
"os"
"github.com/spf13/pflag"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
// Завантаження правил, без налаштувань
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
// Перезапис і налаштування прапорця (аргумент командного рядка)
configOverrides := &clientcmd.ConfigOverrides{}
flags := pflag.NewFlagSet("clientcmddemo", pflag.ExitOnError)
clientcmd.BindOverrideFlags(configOverrides, flags,
clientcmd.RecommendedConfigOverrideFlags(""))
flags.StringVarP(&loadingRules.ExplicitPath, "kubeconfig", "", "", "absolute path(s) to the kubeconfig file(s)")
flags.Parse(os.Args)
// Створення клієнта
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
config, err := kubeConfig.ClientConfig()
if err != nil {
if clientcmd.IsEmptyConfig(err) {
panic("Please provide a configuration pointing to the Kubernetes API server")
}
panic(err)
}
client, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err)
}
// Як дізнатися, який простір імен використовувати
namespace, overridden, err := kubeConfig.Namespace()
if err != nil {
panic(err)
}
fmt.Printf("Chosen namespace: %s; overridden: %t\n", namespace, overridden)
// Використаємо клієнта
nodeList, err := client.CoreV1().Nodes().List(context.TODO(), v1.ListOptions{})
if err != nil {
panic(err)
}
for _, node := range nodeList.Items {
fmt.Println(node.Name)
}
}
Приємного програмування та дякуємо за ваш інтерес до впровадження інструментів зі звичними моделями використання!