Files
sunbeam-charms/tests/local/zaza/sunbeam/charm_tests/k8s/setup.py
Hemanth Nakkina 5c21c23de8 [ci] Add loadbalancer annotations as part of setup
During restarts, the k8s services of type loadbalancer
can get diferent IPs and the integration tests tries to
communicate with older IPs.
Add a step after deploying the bundle to add loadbalancer
annotations to services of type loadbalancer so that
IPs persist during restart of pods.

Pin httpx version in tox.ini due to bug [1] in lightkube

[1] https://github.com/gtsystem/lightkube/issues/78

Change-Id: I013ec1c1e9dbac3ae86c57abcd9d87a3b99f6e82
2024-12-02 21:43:37 +05:30

96 lines
3.3 KiB
Python

# Copyright (c) 2024 Canonical Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
import os
from pathlib import (
Path,
)
import yaml
from lightkube.config.kubeconfig import (
KubeConfig,
)
from lightkube.core.client import Client as KubeClient
from lightkube.core.exceptions import (
ApiError,
ConfigError,
)
from lightkube.resources.core_v1 import (
Service,
)
METALLB_ANNOTATION = "metallb.universe.tf/loadBalancerIPs"
def add_loadbalancer_annotations(
model: str = "openstack", lb_annotation: str = METALLB_ANNOTATION
):
"""Add loadbalancer annotations for all services of type LoadBalancer."""
home: Path = Path(os.environ["HOME"])
kubeconfig_file = home / "kubeconfig"
if not kubeconfig_file.exists():
logging.warning(
"No kubeconfig file present, not adding k8s lb service annotations."
)
return
try:
with kubeconfig_file.open() as f:
kubeconfig = KubeConfig.from_dict(yaml.safe_load(f))
except (AttributeError, KeyError) as e:
logging.warning("Error in kubeconfig content", exc_info=True)
return
try:
kube = KubeClient(kubeconfig, model, trust_env=False)
for service in kube.list(
Service, namespace=model, fields={"spec.type": "LoadBalancer"}
):
if not service.metadata:
logging.warning(f"No metadata for service, {service}")
continue
service_name = str(service.metadata.name)
service_annotations = service.metadata.annotations or {}
if lb_annotation not in service_annotations:
if not service.status:
logging.warning(
f"k8s service {service_name!r} has no status"
)
continue
if not service.status.loadBalancer:
logging.warning(
f"k8s service {service_name!r} has no loadBalancer status"
)
continue
if not service.status.loadBalancer.ingress:
logging.warning(
f"k8s service {service_name!r} has no loadBalancer ingress"
)
continue
loadbalancer_ip = service.status.loadBalancer.ingress[0].ip
service_annotations[lb_annotation] = loadbalancer_ip
service.metadata.annotations = service_annotations
logging.info(
f"Patching {service_name!r} to use IP {loadbalancer_ip!r}"
)
kube.patch(Service, service_name, obj=service)
except ConfigError:
logging.warning("Error creating k8s client", exc_info=True)
except ApiError:
logging.warning("Error getting services list", exc_info=True)