Add configurable support of armada-operator for armada-api

Signed-off-by: Ruslan Aliev <raliev@mirantis.com>
Change-Id: I76fb41062d152bf360a85d781c19ab5b204769b8
This commit is contained in:
Ruslan Aliev 2023-11-07 23:50:43 -06:00
parent 5df69442b0
commit 0167664f53
8 changed files with 698 additions and 135 deletions

View File

@ -94,6 +94,12 @@ path to the private key that includes the name of the key itself.""")),
"""Time in seconds of how much time needs to pass since
the last update of an existing lock before armada forcibly removes it
and tries to acquire its own lock""")),
cfg.BoolOpt(
'enable_operator',
default=False,
help=utils.fmt(
"""Determines whether the operator has to be enabled
to apply charts instead of armada-api itself""")),
]

View File

@ -19,6 +19,13 @@ KEYWORD_GROUPS = 'chart_groups'
KEYWORD_CHARTS = 'chart_group'
KEYWORD_RELEASE = 'release'
CHART_VERSION = 'v1'
CHART_APIVERSION = 'armada.airshipit.io/v1'
CHART_GROUP = 'armada.airshipit.io'
CHART_KIND = 'ArmadaChart'
CHART_PLURAL = 'armadacharts'
CHART_LABEL = 'armada.airshipit.io/release-name'
# Armada
DEFAULT_CHART_TIMEOUT = 900
DEFAULT_TEST_TIMEOUT = 300

View File

@ -14,8 +14,10 @@
from concurrent.futures import ThreadPoolExecutor, as_completed
from kubernetes.client.rest import ApiException
from oslo_config import cfg
from oslo_log import log as logging
import yaml
from armada import const
from armada.conf import set_current_chart
@ -74,6 +76,7 @@ class Armada(object):
'''
self.enable_chart_cleanup = enable_chart_cleanup
self.enable_operator = CONF.enable_operator
self.force_wait = force_wait
self.helm = helm
try:
@ -96,12 +99,42 @@ class Armada(object):
"""
LOG.info("Performing pre-flight operations.")
# Clone the chart sources
if self.enable_operator:
try:
self.helm.k8s.read_custom_resource_definition(
"armadacharts.armada.airshipit.io")
except ApiException as err:
if err.status == 404:
LOG.info(
"ArmadaCharts Custom Resource Definition not found, "
"creating now")
with open("armadacharts_crd.yaml") as crd_file:
body = yaml.load(crd_file, Loader=yaml.CSafeLoader)
self.helm.k8s.create_custom_resource_definition(body)
else:
raise
manifest_data = self.manifest.get(const.KEYWORD_DATA, {})
for group in manifest_data.get(const.KEYWORD_GROUPS, []):
for ch in group.get(const.KEYWORD_DATA).get(const.KEYWORD_CHARTS,
[]):
self.chart_download.get_chart(ch, manifest=self.manifest)
if self.enable_operator:
# Create namespaces if not exist
chart = ch.get(const.KEYWORD_DATA)
ns = chart.get('namespace', '')
if ns != "":
try:
self.helm.k8s.read_namespace(ns)
except ApiException as err:
if err.status == 404:
LOG.info(
"Unable to find namespace '%s', creating",
ns)
self.helm.k8s.create_namespace(ns)
else:
raise
else:
self.chart_download.get_chart(ch, manifest=self.manifest)
def sync(self):
'''
@ -195,7 +228,8 @@ class Armada(object):
# End of Charts in ChartGroup
LOG.info('All Charts applied in ChartGroup %s.', cg_name)
self.post_flight_ops()
if self.enable_operator:
self.post_flight_ops()
if self.enable_chart_cleanup:
self._chart_cleanup(

View File

@ -15,6 +15,8 @@
import os
import time
from kubernetes.client.rest import ApiException
from oslo_config import cfg
from oslo_log import log as logging
from armada import const
@ -31,6 +33,7 @@ from armada.handlers.wait import ChartWait
import armada.utils.release as r
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
class ChartDeploy(object):
@ -44,6 +47,8 @@ class ChartDeploy(object):
self.k8s_wait_attempt_sleep = k8s_wait_attempt_sleep
self.timeout = timeout
self.helm = helm
self.k8s = helm.k8s
self.enable_operator = CONF.enable_operator
def execute(self, ch, cg_test_all_charts, prefix, concurrency):
chart_name = ch['metadata']['name']
@ -52,7 +57,7 @@ class ChartDeploy(object):
chart_name):
return self._execute(ch, cg_test_all_charts, prefix)
def _execute(self, ch, cg_test_all_charts, prefix):
def _execute(self, ch, cg_test_all_charts, prefix): # noqa: C901
manifest_name = self.manifest['metadata']['name']
chart = ch[const.KEYWORD_DATA]
chart_name = ch['metadata']['name']
@ -60,8 +65,10 @@ class ChartDeploy(object):
release = chart.get('release')
release_name = r.release_prefixer(prefix, release)
release_id = helm.HelmReleaseId(namespace, release_name)
source_dir = chart['source_dir']
source_directory = os.path.join(*source_dir)
if not self.enable_operator:
source_dir = chart['source_dir']
source_directory = os.path.join(*source_dir)
LOG.info('Processing Chart, release=%s', release_id)
result = {}
@ -77,8 +84,12 @@ class ChartDeploy(object):
# Begin Chart timeout deadline
deadline = time.time() + wait_timeout
old_release = self.helm.release_metadata(release_id)
action = metrics.ChartDeployAction.NOOP
if not self.enable_operator:
old_release = self.helm.release_metadata(release_id)
action = metrics.ChartDeployAction.NOOP
else:
old_release = None
action = None
def noop():
pass
@ -87,127 +98,140 @@ class ChartDeploy(object):
# Resolve action
values = chart.get('values', {})
pre_actions = {}
if not self.enable_operator:
pre_actions = {}
status = None
if old_release:
status = r.get_release_status(old_release)
status = None
if old_release:
status = r.get_release_status(old_release)
native_wait_enabled = chart_wait.is_native_enabled()
native_wait_enabled = chart_wait.is_native_enabled()
chartbuilder = ChartBuilder.from_chart_doc(ch, self.helm)
chartbuilder = ChartBuilder.from_chart_doc(ch, self.helm)
if status == helm.STATUS_DEPLOYED:
if status == helm.STATUS_DEPLOYED:
# indicate to the end user what path we are taking
LOG.info("Existing release %s found", release_id)
# indicate to the end user what path we are taking
LOG.info("Existing release %s found", release_id)
# extract the installed chart and installed values from the
# latest release so we can compare to the intended state
old_chart = old_release['chart']
old_values = old_release.get('config', {})
# extract the installed chart and installed values from the
# latest release so we can compare to the intended state
old_chart = old_release['chart']
old_values = old_release.get('config', {})
upgrade = chart.get('upgrade', {})
options = upgrade.get('options', {})
upgrade = chart.get('upgrade', {})
options = upgrade.get('options', {})
# TODO: Remove when v1 doc support is removed.
schema_info = get_schema_info(ch['schema'])
if schema_info.version < 2:
no_hooks_location = upgrade
# TODO: Remove when v1 doc support is removed.
schema_info = get_schema_info(ch['schema'])
if schema_info.version < 2:
no_hooks_location = upgrade
else:
no_hooks_location = options
disable_hooks = no_hooks_location.get('no_hooks', False)
force = options.get('force', False)
if upgrade:
upgrade_pre = upgrade.get('pre', {})
upgrade_post = upgrade.get('post', {})
if not self.disable_update_pre and upgrade_pre:
pre_actions = upgrade_pre
if not self.disable_update_post and upgrade_post:
LOG.warning(
'Post upgrade actions are ignored by Armada'
'and will not affect deployment.')
LOG.info('Checking for updates to chart release inputs.')
new_chart = chartbuilder.get_helm_chart(release_id, values)
diff = self.get_diff(old_chart, old_values, new_chart, values)
if not diff:
LOG.info("Found no updates to chart release inputs")
else:
action = metrics.ChartDeployAction.UPGRADE
LOG.info("Found updates to chart release inputs")
def upgrade():
# do actual update
timer = int(round(deadline - time.time()))
PreUpdateActions(self.helm.k8s).execute(
pre_actions, release, namespace, chart,
disable_hooks, values, timer)
LOG.info(
"Upgrading release=%s, wait=%s, "
"timeout=%ss", release_id, native_wait_enabled,
timer)
self.helm.upgrade_release(
source_directory,
release_id,
disable_hooks=disable_hooks,
values=values,
wait=native_wait_enabled,
timeout=timer,
force=force)
LOG.info('Upgrade completed')
result['upgrade'] = release_id
deploy = upgrade
else:
no_hooks_location = options
disable_hooks = no_hooks_location.get('no_hooks', False)
force = options.get('force', False)
if upgrade:
upgrade_pre = upgrade.get('pre', {})
upgrade_post = upgrade.get('post', {})
if not self.disable_update_pre and upgrade_pre:
pre_actions = upgrade_pre
if not self.disable_update_post and upgrade_post:
LOG.warning(
'Post upgrade actions are ignored by Armada'
'and will not affect deployment.')
LOG.info('Checking for updates to chart release inputs.')
new_chart = chartbuilder.get_helm_chart(release_id, values)
diff = self.get_diff(old_chart, old_values, new_chart, values)
if not diff:
LOG.info("Found no updates to chart release inputs")
else:
action = metrics.ChartDeployAction.UPGRADE
LOG.info("Found updates to chart release inputs")
def upgrade():
# do actual update
def install():
timer = int(round(deadline - time.time()))
PreUpdateActions(self.helm.k8s).execute(
pre_actions, release, namespace, chart, disable_hooks,
values, timer)
LOG.info(
"Upgrading release=%s, wait=%s, "
"Installing release=%s, wait=%s, "
"timeout=%ss", release_id, native_wait_enabled, timer)
self.helm.upgrade_release(
self.helm.install_release(
source_directory,
release_id,
disable_hooks=disable_hooks,
values=values,
wait=native_wait_enabled,
timeout=timer,
force=force)
timeout=timer)
LOG.info('Upgrade completed')
result['upgrade'] = release_id
LOG.info('Install completed')
result['install'] = release_id
deploy = upgrade
else:
# Check for release with status other than DEPLOYED
if status:
if status != helm.STATUS_FAILED:
LOG.warn(
'Unexpected release status encountered '
'release=%s, status=%s', release_id, status)
# Make best effort to determine whether a deployment is
# likely pending, by checking if the last deployment
# was started within the timeout window of the chart.
last_deployment_age = r.get_last_deployment_age(
old_release)
likely_pending = last_deployment_age <= wait_timeout
if likely_pending:
# We don't take any deploy action and wait for the
# to get deployed.
deploy = noop
deadline = deadline - last_deployment_age
else:
# Release is likely stuck in an unintended
# state. Log and continue on with remediation steps
# below.
LOG.info(
'Old release %s likely stuck in status %s, '
'(last deployment age=%ss) >= '
'(chart wait timeout=%ss)', release, status,
last_deployment_age, wait_timeout)
res = self.purge_release(
chart, release_id, status, manifest_name,
chart_name, result)
def install():
timer = int(round(deadline - time.time()))
LOG.info(
"Installing release=%s, wait=%s, "
"timeout=%ss", release_id, native_wait_enabled, timer)
self.helm.install_release(
source_directory,
release_id,
values=values,
wait=native_wait_enabled,
timeout=timer)
LOG.info('Install completed')
result['install'] = release_id
# Check for release with status other than DEPLOYED
if status:
if status != helm.STATUS_FAILED:
LOG.warn(
'Unexpected release status encountered '
'release=%s, status=%s', release_id, status)
# Make best effort to determine whether a deployment is
# likely pending, by checking if the last deployment
# was started within the timeout window of the chart.
last_deployment_age = r.get_last_deployment_age(
old_release)
likely_pending = last_deployment_age <= wait_timeout
if likely_pending:
# We don't take any deploy action and wait for the
# to get deployed.
deploy = noop
deadline = deadline - last_deployment_age
if isinstance(res, dict):
if 'protected' in res:
return res
action = metrics.ChartDeployAction.INSTALL
deploy = install
else:
# Release is likely stuck in an unintended
# state. Log and continue on with remediation steps
# below.
LOG.info(
'Old release %s likely stuck in status %s, '
'(last deployment age=%ss) >= '
'(chart wait timeout=%ss)', release, status,
last_deployment_age, wait_timeout)
# The chart is in Failed state, hence we purge
# the chart and attempt to install it again.
res = self.purge_release(
chart, release_id, status, manifest_name,
chart_name, result)
@ -216,21 +240,79 @@ class ChartDeploy(object):
return res
action = metrics.ChartDeployAction.INSTALL
deploy = install
else:
# The chart is in Failed state, hence we purge
# the chart and attempt to install it again.
res = self.purge_release(
chart, release_id, status, manifest_name, chart_name,
result)
if isinstance(res, dict):
if 'protected' in res:
return res
if status is None:
action = metrics.ChartDeployAction.INSTALL
deploy = install
else:
self._convert_min_ready(chart)
try:
res = self.k8s.read_custom_resource(
group=const.CHART_GROUP,
version=const.CHART_VERSION,
namespace=namespace,
plural=const.CHART_PLURAL,
name=release_name)
# indicate to the end user what path we are taking
LOG.info("Existing release %s found", release_name)
def upgrade():
# do actual update
timer = int(round(deadline - time.time()))
LOG.info(
"Upgrading (replacing 1) release=%s, "
"timeout=%ss", release_name, timer)
self.k8s.replace_custom_resource(
"armada.airshipit.io", "v1", namespace, "armadacharts",
release_name,
self._gen_crd_template(
chart_name, release_id,
chart['source']['location'], values,
chart.get('wait', {}),
chart.get('upgrade', {}).get('pre',
{}).get('delete', []),
chart.get('test', {}).get('enabled', True),
res.get('metadata',
{}).get('resourceVersion', None)))
LOG.info('Upgrade completed')
result['upgrade'] = release_id
action = metrics.ChartDeployAction.UPGRADE
deploy = upgrade
except ApiException as err:
if err.status == 404:
def install():
timer = int(round(deadline - time.time()))
LOG.info(
"Installing release=%s, "
"timeout=%ss", release_name, timer)
self.k8s.create_custom_resource(
const.CHART_GROUP, const.CHART_VERSION, namespace,
const.CHART_PLURAL,
self._gen_crd_template(
chart_name, release_id,
chart['source']['location'], values,
chart.get('wait', {}),
chart.get('upgrade',
{}).get('pre', {}).get('delete', []),
chart.get('test', {}).get('enabled', True)))
LOG.info('Install completed')
result['install'] = release_id
action = metrics.ChartDeployAction.INSTALL
deploy = install
if status is None:
action = metrics.ChartDeployAction.INSTALL
deploy = install
else:
raise
# Deploy
with metrics.CHART_DEPLOY.get_context(wait_timeout, manifest_name,
@ -240,21 +322,30 @@ class ChartDeploy(object):
# Wait
timer = int(round(deadline - time.time()))
chart_wait.wait(timer)
if self.enable_operator:
self.k8s.wait_custom_resource(
const.CHART_GROUP, const.CHART_VERSION, namespace,
const.CHART_PLURAL,
"{}={}".format(const.CHART_LABEL, release_id.name), timer)
else:
chart_wait.wait(timer)
# Test
just_deployed = ('install' in result) or ('upgrade' in result)
last_test_passed = old_release and r.get_last_test_result(old_release)
test_handler = Test(
chart, release_id, self.helm, cg_test_charts=cg_test_all_charts)
run_test = test_handler.test_enabled and (
just_deployed or not last_test_passed)
if run_test:
with metrics.CHART_TEST.get_context(test_handler.timeout,
manifest_name, chart_name):
self._test_chart(test_handler)
if not self.enable_operator:
# Test
just_deployed = ('install' in result) or ('upgrade' in result)
last_test_passed = old_release and r.get_last_test_result(
old_release)
test_handler = Test(
chart,
release_id,
self.helm,
cg_test_charts=cg_test_all_charts)
run_test = test_handler.test_enabled and (
just_deployed or not last_test_passed)
if run_test:
with metrics.CHART_TEST.get_context(test_handler.timeout,
manifest_name, chart_name):
self._test_chart(test_handler)
return result
@ -292,3 +383,56 @@ class ChartDeploy(object):
def get_diff(self, old_chart, old_values, new_chart, values):
return ReleaseDiff(old_chart, old_values, new_chart, values).get_diff()
@staticmethod
def _convert_min_ready(chart):
wait_opts = chart.get('wait', {})
if len(wait_opts) > 0:
res_opts = wait_opts.get('resources', [])
for opt in res_opts:
if 'min_ready' in opt:
opt['min_ready'] = str(opt['min_ready'])
@staticmethod
def _gen_crd_template(
chart_name,
release_id,
location,
values,
wait_opts,
pre_opts=None,
test_enabled=True,
res_ver=None):
if pre_opts is None:
pre_opts = []
obj = {
'apiVersion': const.CHART_APIVERSION,
'kind': const.CHART_KIND,
'metadata': {
'labels': {
const.CHART_LABEL: release_id.name
},
'name': release_id.name,
'namespace': release_id.namespace,
},
'data': {
'chart_name': chart_name,
'namespace': release_id.namespace,
'release': release_id.name,
'source': {
'location': location,
},
'values': values,
'test': {
'enabled': test_enabled
},
'delete': pre_opts,
'wait': wait_opts,
}
}
if res_ver is not None:
obj['metadata']['resourceVersion'] = res_ver
return obj

View File

@ -21,8 +21,11 @@ from kubernetes import watch
from kubernetes.client.rest import ApiException
from oslo_config import cfg
from oslo_log import log as logging
from retry import retry
import urllib3
from armada.const import DEFAULT_K8S_TIMEOUT
from armada.exceptions import armada_exceptions
from armada.exceptions import k8s_exceptions as exceptions
CONF = cfg.CONF
@ -361,6 +364,13 @@ class K8s(object):
timeout = DEFAULT_K8S_TIMEOUT
return timeout
def create_namespace(self, namespace):
return self.client.create_namespace(
client.V1Namespace(metadata=client.V1ObjectMeta(name=namespace)))
def read_namespace(self, namespace):
return self.client.read_namespace(namespace)
def create_custom_resource_definition(self, crd):
"""Creates a custom resource definition
@ -420,6 +430,9 @@ class K8s(object):
return self.custom_objects.get_namespaced_custom_object(
group, version, namespace, plural, name)
def read_custom_resource_definition(self, name):
return self.api_extensions.read_custom_resource_definition(name)
def replace_custom_resource(
self, group, version, namespace, plural, name, body):
"""Replaces a custom resource
@ -436,3 +449,62 @@ class K8s(object):
"""
return self.custom_objects.replace_namespaced_custom_object(
group, version, namespace, plural, name, body)
def patch_custom_resource(
self, group, version, namespace, plural, name, body):
return self.custom_objects.patch_namespaced_custom_object(
group, version, namespace, plural, name, body)
@retry(
exceptions=(
urllib3.exceptions.ProtocolError,
urllib3.exceptions.MaxRetryError),
delay=1)
def wait_custom_resource(
self, group, version, namespace, plural, label, timeout=300):
LOG.info(
'starting to wait following params %s %s %s %s %s %s', group,
version, namespace, plural, label, timeout)
resources = self.custom_objects.list_namespaced_custom_object(
group, version, namespace, plural, label_selector=label)
if len(resources.get('items', [])) != 1:
LOG.warn('No resources found')
raise armada_exceptions.WaitException(
"Unexpected number of objects to wait")
def is_resource_ready(status):
conds = status.get('conditions', [])
for cond in conds:
if cond['type'] == "Ready" and cond['status'] == "True":
if status['observedGeneration'] == \
event['object']['metadata']['generation']:
LOG.info('Resource is ready %s', resource_name)
return True
return False
for resource in resources.get('items', []):
resource_name = resource['metadata']['name']
if is_resource_ready(resource.get('status', {})):
LOG.warn("nothing to watch, exiting")
return
w = watch.Watch()
for event in w.stream(
self.custom_objects.list_namespaced_custom_object, group,
version, namespace, plural, label_selector=label,
timeout_seconds=timeout):
obj_status = event['object'].get('status', {})
if obj_status == {}:
continue
LOG.info('got event object %s ', obj_status)
resource_name = event['object']['metadata']['name']
if is_resource_ready(obj_status):
return
raise armada_exceptions.ArmadaTimeoutException(
"Resource with label {} is not ready".format(label))

259
armadacharts_crd.yaml Normal file
View File

@ -0,0 +1,259 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.13.0
name: armadacharts.armada.airshipit.io
spec:
group: armada.airshipit.io
names:
kind: ArmadaChart
listKind: ArmadaChartList
plural: armadacharts
singular: armadachart
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: ArmadaChart is the Schema for the armadacharts API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
data:
description: ArmadaChartSpec defines the desired state of ArmadaChart
properties:
chart_name:
description: ChartName is an example field of ArmadaChart. Edit armadachart_types.go
to remove/update
type: string
delete:
description: Delete holds the values for this Helm release.
items:
description: ArmadaChartDeleteResource defines the wait options
of ArmadaChart
properties:
labels:
additionalProperties:
type: string
description: Labels is an example field of ArmadaChart. Edit
armadachart_types.go to remove/update
type: object
type:
description: Type is an example field of ArmadaChart. Edit armadachart_types.go
to remove/update
type: string
type: object
type: array
namespace:
description: Namespace is an example field of ArmadaChart. Edit armadachart_types.go
to remove/update
type: string
release:
description: Release is an example field of ArmadaChart. Edit armadachart_types.go
to remove/update
type: string
source:
description: Source is an example field of ArmadaChart. Edit armadachart_types.go
to remove/update
properties:
location:
description: Location is an example field of ArmadaChart. Edit
armadachart_types.go to remove/update
type: string
subpath:
description: Subpath is an example field of ArmadaChart. Edit
armadachart_types.go to remove/update
type: string
type:
description: Type is an example field of ArmadaChart. Edit armadachart_types.go
to remove/update
type: string
type: object
test:
description: Test holds the values for this Helm release.
properties:
enabled:
description: Enabled is an example field of ArmadaChart. Edit
armadachart_types.go to remove/update
type: boolean
type: object
values:
description: Values holds the values for this Helm release.
x-kubernetes-preserve-unknown-fields: true
wait:
description: Wait holds the values for this Helm release.
properties:
labels:
additionalProperties:
type: string
description: Labels is an example field of ArmadaChart. Edit armadachart_types.go
to remove/update
type: object
native:
description: ArmadaChartWaitNative defines the wait options of
ArmadaChart
properties:
enabled:
description: Enabled is an example field of ArmadaChart. Edit
armadachart_types.go to remove/update
type: boolean
type: object
resources:
items:
description: ArmadaChartWaitResource defines the wait options
of ArmadaChart
properties:
labels:
additionalProperties:
type: string
description: Labels is an example field of ArmadaChart.
Edit armadachart_types.go to remove/update
type: object
min_ready:
type: string
type:
description: Type is an example field of ArmadaChart. Edit
armadachart_types.go to remove/update
type: string
type: object
type: array
timeout:
description: Timeout is the time to wait for full reconciliation
of Helm release.
type: integer
type: object
type: object
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
status:
description: ArmadaChartStatus defines the observed state of ArmadaChart
properties:
conditions:
description: Conditions holds the conditions for the ArmadaChart.
items:
description: "Condition contains details for one aspect of the current
state of this API Resource. --- This struct is intended for direct
use as an array at the field path .status.conditions. For example,
\n type FooStatus struct{ // Represents the observations of a
foo's current state. // Known .status.conditions.type are: \"Available\",
\"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
// +listType=map // +listMapKey=type Conditions []metav1.Condition
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
transitioned from one status to another. This should be when
the underlying condition changed. If that is not known, then
using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: message is a human readable message indicating
details about the transition. This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: observedGeneration represents the .metadata.generation
that the condition was set based upon. For instance, if .metadata.generation
is currently 12, but the .status.conditions[x].observedGeneration
is 9, the condition is out of date with respect to the current
state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: reason contains a programmatic identifier indicating
the reason for the condition's last transition. Producers
of specific condition types may define expected values and
meanings for this field, and whether the values are considered
a guaranteed API. The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
--- Many .condition.type values are consistent across resources
like Available, but because arbitrary conditions can be useful
(see .node.status.conditions), the ability to deconflict is
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
failures:
description: Failures is the reconciliation failure count against
the latest desired state. It is reset after a successful reconciliation.
format: int64
type: integer
helmChart:
description: HelmChart is the namespaced name of the HelmChart resource
created by the controller for the ArmadaChart.
type: string
installFailures:
description: InstallFailures is the install failure count against
the latest desired state. It is reset after a successful reconciliation.
format: int64
type: integer
lastAppliedRevision:
description: LastAppliedRevision is the revision of the last successfully
applied source.
type: string
lastAttemptedRevision:
description: LastAttemptedRevision is the revision of the last reconciliation
attempt.
type: string
lastAttemptedValuesChecksum:
description: LastAttemptedValuesChecksum is the SHA1 checksum of the
values of the last reconciliation attempt.
type: string
lastHandledReconcileAt:
description: LastHandledReconcileAt holds the value of the most recent
reconcile request value, so a change of the annotation value can
be detected.
type: string
lastReleaseRevision:
description: LastReleaseRevision is the revision of the last successful
Helm release.
type: integer
observedGeneration:
description: ObservedGeneration is the last observed generation.
format: int64
type: integer
upgradeFailures:
description: UpgradeFailures is the upgrade failure count against
the latest desired state. It is reset after a successful reconciliation.
format: int64
type: integer
type: object
type: object
served: true
storage: true
subresources:
status: {}

View File

@ -109,6 +109,45 @@ spec:
{{ tuple $envAll "api" $mounts_armada_api_init | include "helm-toolkit.snippets.kubernetes_entrypoint_init_container" | indent 8 }}
{{ dict "envAll" $envAll "application" "armada" "container" "armada_api_init" | include "helm-toolkit.snippets.kubernetes_container_security_context" | indent 10 }}
containers:
{{- if .Values.conf.armada.enable_operator }}
- name: manager
{{ tuple $envAll "operator" | include "helm-toolkit.snippets.image" | indent 10 }}
{{ tuple $envAll $envAll.Values.pod.resources.api | include "helm-toolkit.snippets.kubernetes_resources" | indent 10 }}
command:
- /manager
args:
- '--health-probe-bind-address=:8081'
- '--metrics-bind-address=127.0.0.1:8080'
- '--leader-elect'
livenessProbe:
httpGet:
path: /healthz
port: 8081
scheme: HTTP
initialDelaySeconds: 15
timeoutSeconds: 1
periodSeconds: 20
successThreshold: 1
failureThreshold: 3
readinessProbe:
httpGet:
path: /readyz
port: 8081
scheme: HTTP
initialDelaySeconds: 5
timeoutSeconds: 1
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: IfNotPresent
securityContext:
capabilities:
drop:
- ALL
allowPrivilegeEscalation: false
{{- end }}
- name: armada-api
{{ tuple $envAll "api" | include "helm-toolkit.snippets.image" | indent 10 }}
{{ tuple $envAll $envAll.Values.pod.resources.api | include "helm-toolkit.snippets.kubernetes_resources" | indent 10 }}

View File

@ -29,6 +29,7 @@ labels:
images:
tags:
api: 'quay.io/airshipit/armada:latest'
operator: 'quay.io/raliev12/armada-controller:latest'
dep_check: 'quay.io/stackanetes/kubernetes-entrypoint:v0.3.1'
ks_endpoints: 'docker.io/openstackhelm/heat:newton'
ks_service: 'docker.io/openstackhelm/heat:newton'
@ -180,6 +181,7 @@ conf:
delay_auth_decision: true
oslo_policy:
policy_file: policy.yaml
enable_operator: false
paste:
'app:armada-api':
paste.app_factory: 'armada.api.server:paste_start_armada'