Статьи

Fabric8 Kubernetes и Openshift Java DSL

вступление

В первых выпусках Fabric8 v2 использовался клиент Kubernetes на основе JAX-RS, который использовал ApacheCXF . Клиент был великолепен, но мы всегда хотели предложить что-то более тонкое, с меньшим количеством зависимостей (чтобы его было легче принять) . Мы также хотели дать ему возможность поднять его и построить вокруг него DSL, чтобы его было легче использовать и читать.

Новый клиент в настоящее время живет по адресу: https://github.com/fabric8io/kubernetes-client и предоставляет следующие модули:

  1. Клиент Kubernetes .
  2. Клиент Openshift .
  3. Поддельные рамки для всего вышеперечисленного (на основе EasyMock )

Первый взгляд на клиента

Давайте кратко рассмотрим, как вы можете создавать, перечислять и удалять вещи с помощью клиента:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//Instantiate the client
KubernetesClient client = new DefaultKubernetesClient();
 
//Create a service
Service myservice = ...;
client.services().inNamespace("fabric8").create(myservice);
 
//Create a service inline
Service jenkins = client.services().inNamespace("fabric8").createNew()
        .withNewMetadata()
            .withName("jenkins")
            .addToLabels("component", "jenkins")
        .endMetadata()
        .done();
 
//List services
ServiceList serviceList = client.services().inNamespace("fabric8").list();
 
//Watch services
client.services().inNamespace("fabric8").watch(new Watcher<Service>() {
        @Override
        public void eventReceived(Action action, Service resource) {
          logger.info("{}: {}", action, resource);
        }
});
 
//Delete by label
Boolean deleted = client.services().withLabel("component", "jenkins").delete();
 
//Close client
client.close();

Вышеприведенный фрагмент в значительной степени говорит само за себя (и в этом прелесть использования DSL), но у меня все еще есть пост в блоге, поэтому я предоставлю как можно больше подробностей.

Модель клиентского домена

Вы можете думать о клиенте как о соединении двух вещей:

  1. Доменная модель Кубернетеса .
  2. DSL вокруг модели.

Модель предметной области – это набор объектов, представляющих данные, которыми обмениваются клиент и Kubernetes / Openshift . Необработанный формат данных – JSON. Эти объекты JSON довольно сложны, а их структура довольно строгая, поэтому создание их вручную – не тривиальная задача.

Нам нужно было иметь способ манипулировать этими объектами JSON в Java (и иметь возможность воспользоваться преимуществами завершения кода и т. Д.), Но также максимально приближаться к исходному формату. Использование POJO-представления объектов JSON можно использовать для манипуляций, но оно не совсем похоже на JSON, а также не совсем подходит для JSON с глубоким вложением. Поэтому вместо этого мы решили создать гибкие компоновщики поверх тех POJO, которые использовали точно такую ​​же структуру с оригинальным JSON.

Например, вот объект JSON службы Kubernetes :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
  "kind": "Service",
  "metadata": {
    "name": "kubernetes",
    "namespace": "default",
    "labels": {
      "component": "apiserver",
      "provider": "kubernetes"
    }
  },
  "spec": {
    "ports": [
      {
        "name": "",
        "protocol": "TCP",
        "port": 443,
        "targetPort": 443
      }
    ],
    "selector": null,
    "portalIP": "172.30.17.2",
    "sessionAffinity": "None"
  },
  "status": {}
}

Java-эквивалент с использованием Fluent Builders может быть:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
Service srv = new ServiceBuilder()
                .withNewMetadata()
                    .withName("kubernetes")
                    .addToLabels("component", "apiserver")
                    .addToLabels("provider", "kubernetes")
                .endMetadata()
                .withNewSpec()
                    .addNewPort()
                        .withProtocol("TCP")
                        .withPort(443)
                        .withNewTargetPort(443)
                    .endPort()
                    .withPortalIP("172.30.17.2")
                    .withSessionAffinity("None")
                .endSpec()
                .build();

Модель предметной области основана на собственном проекте: модель Кубернетеса из Fabric8 . Модель генерируется из кода Kubernetes и Openshift после долгого процесса:

  1. Перейти исходная схема преобразования JSON
  2. Преобразование схемы JSON POJO
  3. Поколение Свободных Строителей

Свободные строители созданы с помощью крошечного проекта sundrio , о котором я расскажу в следующем посте.

Получение экземпляра клиента

Получение экземпляра клиентского экземпляра по умолчанию довольно тривиально, поскольку предоставляется пустой конструктор. Когда используется пустой конструктор, клиент будет использовать настройки по умолчанию:

  • URL Kubernetes
    1. Системное свойство « kubernetes.master »
    2. Переменная среды « KUBERNETES_MASTER »
    3. Из файла « .kube / config » внутри домашнего пользователя.
    4. Использование DNS: « https: //kubernetes.default.svc «
  • Путь к служебной учетной записи « /var/run/secrets/kubernetes.io/serviceaccount/ »

Более точная конфигурация может быть предоставлена ​​путем передачи экземпляра объекта Config .

1
2
3
4
5
6
7
8
//Client with custom config
Config config = new ConfigBuilder()
        .withMasterUrl(url)
        .withTrustCerts(true)
        .withOauthToken(mytoken)
        .build();
         
KubernetesClient = new DefaultKubernetesClient(config);

Клиентские расширения и адаптеры

Для поддержки расширений Kubernetes (например, Openshift ) клиент использует понятие Расширения и Адаптера . Идея довольно проста. Клиент расширения расширяет клиент по умолчанию и реализует Расширение . Каждый экземпляр клиента может быть адаптирован к Расширению, если адаптер можно найти с помощью ServiceLoader Java (извините, отец).

Вот пример того, как адаптировать любой экземпляр клиента к экземпляру OpenshiftClient :

1
2
KubernetesClient client = new DefaultKubernetesClinet();
OpenShiftClient oc = client.adapt(OpenShiftClient.class);

Приведенный выше код будет работать только в том случае, если / oapi существует в списке корневых путей, возвращаемых клиентом Kubernetes (т. Е. Клиент указывает на установку с открытой сменой). Если нет, он выдаст исключение IllegalArugementException.

Если пользователь пишет код, связанный с Openshift, он всегда может напрямую создать экземпляр экземпляра клиента openshift по умолчанию .

1
2
3
4
5
6
7
8
//Openshift client with custom config
OpenshiftConfig config = new OpenshiftConfigBuilder()
          .withMasterUrl(url)
          .withOpenShiftUrl(openshiftUrl)
          .withTrustCerts(true)
          .build();
           
OpenshiftClient client = new DefaultOpenshiftClient(config);

Тестирование и издевательство

Насмешка над клиентом, который разговаривает с внешней системой, является довольно распространенным случаем. Когда клиент плоский
(не поддерживает связывание методов). Насмешка тривиальна, и существуют тонны фреймворков, которые можно использовать для этой работы. Однако при использовании DSL все усложняется и требует много стандартного кода для соединения частей. Если причина не очевидна, давайте просто скажем, что с mocks вы определяете поведение mock для каждого вызова метода. DSL, как правило, имеют гораздо больше методов (с меньшим количеством аргументов) по сравнению с эквивалентными плоскими объектами. Это само по себе увеличивает работу, необходимую для определения поведения. Более того, эти методы соединяются вместе, возвращая промежуточные объекты, а это значит, что их тоже нужно смоделировать, что еще больше увеличивает нагрузку и сложность.

Чтобы удалить весь шаблон и сделать насмешку над клиентом довольно простой для использования, мы объединили DSL клиента с DSL среды для насмешек : EasyMock . Это означает, что точкой входа в этот DSL является сам DSL клиента Kubernetes, но методы терминала были изменены, чтобы они возвращали «Установщики ожиданий». Пример должен облегчить понимание этого.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
KubernetesMockClient mock = new KubernetesMockClient();
  
 //Define the behaviour
 mock.services().inNamespace(or("default","fabric8")).withName("fabric8-console-service").get().andReturn(
                new ServiceBuilder()
                        .withNewMetadata().withName("fabric8-console-service").endMetadata()
                        .withNewSpec()
                            .addNewPort()
                                .withProtocol("TCP")
                                .withPort(80)
                                .withNewTargetPort(9090)
                            .endPort()
                        .endSpec()
                .build()
        ).anyTimes();
         
//Get an instance of the client mock       
KubernetesClient client = mock.replay();
 
//Use the client
Assert.assertNotNull(client.services().inNamespace("fabric8").withName("fabric8-console-service").get());
Assert.assertNotNull(client.services().inNamespace("default").withName("fabric8-console-service").get());
 
//Verify the client
EasyMock.verify(client);

Фреймворк можно легко комбинировать с другими компонентами Fabric8 , такими как расширение CDI . Вам просто нужно создать метод @Produces, который возвращает макет.

Наслаждайтесь!