import os
import yaml
from uuid import uuid4
+from urllib.parse import urlparse
from n2vc.config import EnvironConfig
from n2vc.exceptions import K8sException
cert: str = None,
user: str = None,
password: str = None,
+ oci: bool = False,
):
self.log.debug(
"Cluster {}, adding {} repository {}. URL: {}".format(
# sync local dir
self.fs.sync(from_path=cluster_uuid)
- # helm repo add name url
- command = ("env KUBECONFIG={} {} repo add {} {}").format(
- paths["kube_config"], self._helm_command, quote(name), quote(url)
- )
+ if oci:
+ if user and password:
+ host_port = urlparse(url).netloc if url.startswith("oci://") else url
+ # helm registry login url
+ command = "env KUBECONFIG={} {} registry login {}".format(
+ paths["kube_config"], self._helm_command, quote(host_port)
+ )
+ else:
+ self.log.debug(
+ "OCI registry login is not needed for repo: {}".format(name)
+ )
+ return
+ else:
+ # helm repo add name url
+ command = "env KUBECONFIG={} {} repo add {} {}".format(
+ paths["kube_config"], self._helm_command, quote(name), quote(url)
+ )
if cert:
temp_cert_file = os.path.join(
command=command, raise_exception_on_error=True, env=env
)
- # helm repo update
- command = "env KUBECONFIG={} {} repo update {}".format(
- paths["kube_config"], self._helm_command, quote(name)
- )
- self.log.debug("updating repo: {}".format(command))
- await self._local_async_exec(
- command=command, raise_exception_on_error=False, env=env
- )
+ if not oci:
+ # helm repo update
+ command = "env KUBECONFIG={} {} repo update {}".format(
+ paths["kube_config"], self._helm_command, quote(name)
+ )
+ self.log.debug("updating repo: {}".format(command))
+ await self._local_async_exec(
+ command=command, raise_exception_on_error=False, env=env
+ )
# sync fs
self.fs.reverse_sync(from_path=cluster_uuid)
def _is_helm_chart_a_file(self, chart_name: str):
return chart_name.count("/") > 1
+ @staticmethod
+ def _is_helm_chart_a_url(chart_name: str):
+ result = urlparse(chart_name)
+ return all([result.scheme, result.netloc])
+
async def _install_impl(
self,
cluster_id: str,
cluster_id=cluster_id, params=params
)
- # version
- kdu_model, version = self._split_version(kdu_model)
-
- _, repo = self._split_repo(kdu_model)
- if repo:
- await self.repo_update(cluster_id, repo)
+ kdu_model, version = await self._prepare_helm_chart(kdu_model, cluster_id)
command = self._get_install_command(
kdu_model,
cluster_id=cluster_uuid, params=params
)
- # version
- kdu_model, version = self._split_version(kdu_model)
-
- _, repo = self._split_repo(kdu_model)
- if repo:
- await self.repo_update(cluster_uuid, repo)
+ kdu_model, version = await self._prepare_helm_chart(kdu_model, cluster_uuid)
command = self._get_upgrade_command(
kdu_model,
)
# version
- kdu_model, version = self._split_version(kdu_model)
+ kdu_model, version = await self._prepare_helm_chart(kdu_model, cluster_uuid)
repo_url = await self._find_repo(kdu_model, cluster_uuid)
# add repo
self.log.debug("add repo {}".format(db_repo["name"]))
- if "ca_cert" in db_repo:
- await self.repo_add(
- cluster_uuid,
- db_repo["name"],
- db_repo["url"],
- cert=db_repo["ca_cert"],
- )
- else:
- await self.repo_add(
- cluster_uuid,
- db_repo["name"],
- db_repo["url"],
- )
+ await self.repo_add(
+ cluster_uuid,
+ db_repo["name"],
+ db_repo["url"],
+ cert=db_repo.get("ca_cert"),
+ user=db_repo.get("user"),
+ password=db_repo.get("password"),
+ oci=db_repo.get("oci", False),
+ )
added_repo_dict[repo_id] = db_repo["name"]
except Exception as e:
raise K8sException(
def _split_version(self, kdu_model: str) -> tuple[str, str]:
version = None
- if not self._is_helm_chart_a_file(kdu_model) and ":" in kdu_model:
+ if (
+ not (
+ self._is_helm_chart_a_file(kdu_model)
+ or self._is_helm_chart_a_url(kdu_model)
+ )
+ and ":" in kdu_model
+ ):
parts = kdu_model.split(sep=":")
if len(parts) == 2:
version = str(parts[1])
repo_name = None
idx = kdu_model.find("/")
- if idx >= 0:
+ if not self._is_helm_chart_a_url(kdu_model) and idx >= 0:
chart_name = kdu_model[idx + 1 :]
repo_name = kdu_model[:idx]
return repo_url
+ def _repo_to_oci_url(self, repo):
+ db_repo = self.db.get_one("k8srepos", {"name": repo}, fail_on_empty=False)
+ if db_repo and "oci" in db_repo:
+ return db_repo.get("url")
+
+ async def _prepare_helm_chart(self, kdu_model, cluster_id):
+ # e.g.: "stable/openldap", "1.0"
+ kdu_model, version = self._split_version(kdu_model)
+ # e.g.: "openldap, stable"
+ chart_name, repo = self._split_repo(kdu_model)
+ if repo and chart_name: # repo/chart case
+ oci_url = self._repo_to_oci_url(repo)
+ if oci_url: # oci does not require helm repo update
+ kdu_model = f"{oci_url.rstrip('/')}/{chart_name.lstrip('/')}" # urljoin doesn't work for oci schema
+ else:
+ await self.repo_update(cluster_id, repo)
+ return kdu_model, version
+
async def create_certificate(
self, cluster_uuid, namespace, dns_prefix, name, secret_name, usage
):
self.helm_conn._local_async_exec = asynctest.CoroutineMock(return_value=("", 0))
self.helm_conn._status_kdu = asynctest.CoroutineMock(return_value=None)
self.helm_conn._store_status = asynctest.CoroutineMock()
+ self.helm_conn._repo_to_oci_url = Mock(return_value=None)
self.kdu_instance = "stable-openldap-0005399828"
self.helm_conn.generate_kdu_instance_name = Mock(return_value=self.kdu_instance)
self.helm_conn._get_namespaces = asynctest.CoroutineMock(return_value=[])
}
self.helm_conn._local_async_exec = asynctest.CoroutineMock(return_value=("", 0))
self.helm_conn._store_status = asynctest.CoroutineMock()
+ self.helm_conn._repo_to_oci_url = Mock(return_value=None)
self.helm_conn.get_instance_info = asynctest.CoroutineMock(
return_value=instance_info
)
}
self.helm_conn._local_async_exec = asynctest.CoroutineMock(return_value=("", 0))
self.helm_conn._store_status = asynctest.CoroutineMock()
+ self.helm_conn._repo_to_oci_url = Mock(return_value=None)
self.helm_conn.get_instance_info = asynctest.CoroutineMock(
return_value=instance_info
)
self.helm_conn.values_kdu = asynctest.CoroutineMock(return_value=kdu_values)
self.helm_conn._local_async_exec = asynctest.CoroutineMock(return_value=("", 0))
self.helm_conn._store_status = asynctest.CoroutineMock()
+ self.helm_conn._repo_to_oci_url = Mock(return_value=None)
self.helm_conn.get_instance_info = asynctest.CoroutineMock(
return_value=instance_info
)
"--namespace testk8s --atomic --output yaml --set replicaCount=2 --timeout 1800s "
"--reuse-values --version 1.2.3"
)
- self.helm_conn._local_async_exec.assert_called_once_with(
+ self.helm_conn._local_async_exec.assert_called_with(
command=command, env=self.env, raise_exception_on_error=False
)
# TEST-2
)
self.helm_conn.repo_remove.assert_not_called()
self.helm_conn.repo_add.assert_called_once_with(
- self.cluster_uuid, "bitnami", "https://charts.bitnami.com/bitnami"
+ self.cluster_uuid,
+ "bitnami",
+ "https://charts.bitnami.com/bitnami",
+ cert=None,
+ user=None,
+ password=None,
+ oci=False,
)
self.assertEqual(deleted_repo_list, [], "Deleted repo list should be empty")
self.assertEqual(