Commit cbec62ad authored by Ling Huang's avatar Ling Huang

Add support to ingest log entries to Stackdriver against new "k8s_container" and…

Add support to ingest log entries to Stackdriver against new "k8s_container" and "k8s_node" resources.
parent 9c40f5b5
# This ConfigMap is used to ingest logs against new resources like
# "k8s_container" and "k8s_node" when $LOGGING_STACKDRIVER_RESOURCE_TYPES is set
# to "new".
# When $LOGGING_STACKDRIVER_RESOURCE_TYPES is set to "old", the ConfigMap in
# fluentd-gcp-configmap-old.yaml will be used for ingesting logs against old
# resources like "gke_container" and "gce_instance".
kind: ConfigMap kind: ConfigMap
apiVersion: v1 apiVersion: v1
data: data:
...@@ -17,29 +23,34 @@ data: ...@@ -17,29 +23,34 @@ data:
# "stream":"stderr", # "stream":"stderr",
# "time":"2014-09-25T21:15:03.499185026Z"} # "time":"2014-09-25T21:15:03.499185026Z"}
# #
# The record reformer is used to write the tag to focus on the pod name # The original tag is derived from the log file's location.
# and the Kubernetes container name. For example a Docker container's logs # For example a Docker container's logs might be in the directory:
# might be in the directory:
# /var/lib/docker/containers/997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b # /var/lib/docker/containers/997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b
# and in the file: # and in the file:
# 997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b-json.log # 997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b-json.log
# where 997599971ee6... is the Docker ID of the running container. # where 997599971ee6... is the Docker ID of the running container.
# The Kubernetes kubelet makes a symbolic link to this file on the host machine # The Kubernetes kubelet makes a symbolic link to this file on the host
# in the /var/log/containers directory which includes the pod name and the Kubernetes # machine in the /var/log/containers directory which includes the pod name,
# container name: # the namespace name and the Kubernetes container name:
# synthetic-logger-0.25lps-pod_default-synth-lgr-997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b.log # synthetic-logger-0.25lps-pod_default_synth-lgr-997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b.log
# -> # ->
# /var/lib/docker/containers/997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b/997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b-json.log # /var/lib/docker/containers/997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b/997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b-json.log
# The /var/log directory on the host is mapped to the /var/log directory in the container # The /var/log directory on the host is mapped to the /var/log directory in the container
# running this instance of Fluentd and we end up collecting the file: # running this instance of Fluentd and we end up collecting the file:
# /var/log/containers/synthetic-logger-0.25lps-pod_default-synth-lgr-997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b.log # /var/log/containers/synthetic-logger-0.25lps-pod_default_synth-lgr-997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b.log
# This results in the tag: # This results in the tag:
# var.log.containers.synthetic-logger-0.25lps-pod_default-synth-lgr-997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b.log # var.log.containers.synthetic-logger-0.25lps-pod_default_synth-lgr-997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b.log
# The record reformer is used is discard the var.log.containers prefix and # where 'synthetic-logger-0.25lps-pod' is the pod name, 'default' is the
# the Docker container ID suffix and "kubernetes." is pre-pended giving the tag: # namespace name, 'synth-lgr' is the container name and '997599971ee6..' is
# kubernetes.synthetic-logger-0.25lps-pod_default-synth-lgr # the container ID.
# Tag is then parsed by google_cloud plugin and translated to the metadata, # The record reformer is used to extract pod_name, namespace_name and
# visible in the log viewer # container_name from the tag and set them in a local_resource_id in the
# format of:
# 'k8s_container.<NAMESPACE_NAME>.<POD_NAME>.<CONTAINER_NAME>'.
# The reformer also changes the tags to 'stderr' or 'stdout' based on the
# value of 'stream'.
# local_resource_id is later used by google_cloud plugin to determine the
# monitored resource to ingest logs against.
# Json Log Example: # Json Log Example:
# {"log":"[info:2016-02-16T16:04:05.930-08:00] Some log text here\n","stream":"stdout","time":"2016-02-17T00:04:05.931087621Z"} # {"log":"[info:2016-02-16T16:04:05.930-08:00] Some log text here\n","stream":"stdout","time":"2016-02-17T00:04:05.931087621Z"}
...@@ -49,30 +60,24 @@ data: ...@@ -49,30 +60,24 @@ data:
@type tail @type tail
path /var/log/containers/*.log path /var/log/containers/*.log
pos_file /var/log/gcp-containers.log.pos pos_file /var/log/gcp-containers.log.pos
# Tags at this point are in the format of:
# reform.var.log.containers.<POD_NAME>_<NAMESPACE_NAME>_<CONTAINER_NAME>-<CONTAINER_ID>.log
tag reform.* tag reform.*
read_from_head true read_from_head true
format none format multi_format
<pattern>
format json
time_key time
time_format %Y-%m-%dT%H:%M:%S.%NZ
</pattern>
<pattern>
format /^(?<time>.+) (?<stream>stdout|stderr) [^ ]* (?<log>.*)$/
time_format %Y-%m-%dT%H:%M:%S.%N%:z
</pattern>
</source> </source>
<filter reform.**> <filter reform.**>
@type parser @type parser
key_name message
<parse>
@type multi_format
<pattern>
format json
time_key time
time_format %Y-%m-%dT%H:%M:%S.%NZ
</pattern>
<pattern>
format /^(?<time>.+) (?<stream>stdout|stderr) [^ ]* (?<log>.*)$/
time_format %Y-%m-%dT%H:%M:%S.%N%:z
</pattern>
</parse>
</filter>
<filter reform.**>
@type parser
format /^(?<severity>\w)(?<time>\d{4} [^\s]*)\s+(?<pid>\d+)\s+(?<source>[^ \]]+)\] (?<log>.*)/ format /^(?<severity>\w)(?<time>\d{4} [^\s]*)\s+(?<pid>\d+)\s+(?<source>[^ \]]+)\] (?<log>.*)/
reserve_data true reserve_data true
suppress_parse_error_log true suppress_parse_error_log true
...@@ -83,11 +88,23 @@ data: ...@@ -83,11 +88,23 @@ data:
<match reform.**> <match reform.**>
@type record_reformer @type record_reformer
enable_ruby true enable_ruby true
tag raw.kubernetes.${tag_suffix[4].split('-')[0..-2].join('-')} <record>
# Extract local_resource_id from tag for 'k8s_container' monitored
# resource. The format is:
# 'k8s_container.<namespace_name>.<pod_name>.<container_name>'.
"logging.googleapis.com/local_resource_id" ${"k8s_container.#{tag_suffix[4].rpartition('.')[0].split('_')[1]}.#{tag_suffix[4].rpartition('.')[0].split('_')[0]}.#{tag_suffix[4].rpartition('.')[0].split('_')[2].rpartition('-')[0]}"}
# Rename the field 'log' to a more generic field 'message'. This way the
# fluent-plugin-google-cloud knows to flatten the field as textPayload
# instead of jsonPayload after extracting 'time', 'severity' and
# 'stream' from the record.
message ${record['log']}
</record>
tag ${if record['stream'] == 'stderr' then 'raw.stderr' else 'raw.stdout' end}
remove_keys stream,log
</match> </match>
# Detect exceptions in the log output and forward them as one log entry. # Detect exceptions in the log output and forward them as one log entry.
<match raw.kubernetes.**> <match {raw.stderr,raw.stdout}>
@type detect_exceptions @type detect_exceptions
remove_tag_prefix raw remove_tag_prefix raw
...@@ -350,23 +367,30 @@ data: ...@@ -350,23 +367,30 @@ data:
</metric> </metric>
</filter> </filter>
# This section is exclusive for k8s_container logs. Those come with
# 'stderr'/'stdout' tags.
# TODO(instrumentation): Reconsider this workaround later. # TODO(instrumentation): Reconsider this workaround later.
# Trim the entries which exceed slightly less than 100KB, to avoid # Trim the entries which exceed slightly less than 100KB, to avoid
# dropping them. It is a necessity, because Stackdriver only supports # dropping them. It is a necessity, because Stackdriver only supports
# entries that are up to 100KB in size. # entries that are up to 100KB in size.
<filter kubernetes.**> <filter {stderr,stdout}>
@type record_transformer @type record_transformer
enable_ruby true enable_ruby true
<record> <record>
log ${record['log'].length > 100000 ? "[Trimmed]#{record['log'][0..100000]}..." : record['log']} message ${record['message'].length > 100000 ? "[Trimmed]#{record['message'][0..100000]}..." : record['message']}
</record> </record>
</filter> </filter>
# We use 2 output stanzas - one to handle the container logs and one to handle # Do not collect fluentd's own logs to avoid infinite loops.
# the node daemon logs, the latter of which explicitly sends its logs to the <match fluent.**>
# compute.googleapis.com service rather than container.googleapis.com to keep @type null
# them separate since most users don't care about the node logs. </match>
<match kubernetes.**>
# This section is exclusive for k8s_container logs. These logs come with
# 'stderr'/'stdout' tags.
# We use a separate output stanza for 'k8s_node' logs with a smaller buffer
# because node logs are less important than user's container logs.
<match {stderr,stdout}>
@type google_cloud @type google_cloud
# Try to detect JSON formatted log entries. # Try to detect JSON formatted log entries.
...@@ -397,15 +421,23 @@ data: ...@@ -397,15 +421,23 @@ data:
# Use multiple threads for processing. # Use multiple threads for processing.
num_threads 2 num_threads 2
use_grpc true use_grpc true
labels { # Use Metadata Agent to get monitored resource.
# The logging backend will take responsibility for double writing to enable_metadata_agent true
# the necessary resource types when this label is set.
"logging.googleapis.com/k8s_compatibility": "true"
}
</match> </match>
# Keep a smaller buffer here since these logs are less important than the user's # Attach local_resource_id for 'k8s_node' monitored resource.
# container logs. <filter **>
@type record_transformer
enable_ruby true
<record>
"logging.googleapis.com/local_resource_id" ${"k8s_node.#{ENV['NODE_NAME']}"}
</record>
</filter>
# This section is exclusive for 'k8s_node' logs. These logs come with tags
# that are neither 'stderr' or 'stdout'.
# We use a separate output stanza for 'k8s_container' logs with a larger
# buffer because user's container logs are more important than node logs.
<match **> <match **>
@type google_cloud @type google_cloud
...@@ -425,14 +457,11 @@ data: ...@@ -425,14 +457,11 @@ data:
disable_retry_limit disable_retry_limit
num_threads 2 num_threads 2
use_grpc true use_grpc true
labels { # Use Metadata Agent to get monitored resource.
# The logging backend will take responsibility for double writing to enable_metadata_agent true
# the necessary resource types when this label is set.
"logging.googleapis.com/k8s_compatibility": "true"
}
</match> </match>
metadata: metadata:
name: fluentd-gcp-config-v1.2.4 name: fluentd-gcp-config-v1.2.5
namespace: kube-system namespace: kube-system
labels: labels:
addonmanager.kubernetes.io/mode: Reconcile addonmanager.kubernetes.io/mode: Reconcile
...@@ -37,6 +37,14 @@ spec: ...@@ -37,6 +37,14 @@ spec:
readOnly: true readOnly: true
- name: config-volume - name: config-volume
mountPath: /etc/google-fluentd/config.d mountPath: /etc/google-fluentd/config.d
env:
- name: STACKDRIVER_METADATA_AGENT_URL
value: {{ stackdriver_metadata_agent_url }}
- name: NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
# Liveness probe is aimed to help in situarions where fluentd # Liveness probe is aimed to help in situarions where fluentd
# silently hangs for no apparent reasons until manual restart. # silently hangs for no apparent reasons until manual restart.
# The idea of this probe is that if fluentd is not queueing or # The idea of this probe is that if fluentd is not queueing or
...@@ -108,4 +116,4 @@ spec: ...@@ -108,4 +116,4 @@ spec:
path: /var/lib/docker/containers path: /var/lib/docker/containers
- name: config-volume - name: config-volume
configMap: configMap:
name: fluentd-gcp-config-v1.2.4 name: {{ fluentd_gcp_configmap_name }}-v1.2.5
...@@ -378,7 +378,7 @@ if [[ -n "${LOGROTATE_MAX_SIZE:-}" ]]; then ...@@ -378,7 +378,7 @@ if [[ -n "${LOGROTATE_MAX_SIZE:-}" ]]; then
fi fi
# Fluentd requirements # Fluentd requirements
FLUENTD_GCP_VERSION="${FLUENTD_GCP_VERSION:-0.2-1.5.28-1}" FLUENTD_GCP_VERSION="${FLUENTD_GCP_VERSION:-0.2-1.5.30-1-k8s}"
FLUENTD_GCP_MEMORY_LIMIT="${FLUENTD_GCP_MEMORY_LIMIT:-}" FLUENTD_GCP_MEMORY_LIMIT="${FLUENTD_GCP_MEMORY_LIMIT:-}"
FLUENTD_GCP_CPU_REQUEST="${FLUENTD_GCP_CPU_REQUEST:-}" FLUENTD_GCP_CPU_REQUEST="${FLUENTD_GCP_CPU_REQUEST:-}"
FLUENTD_GCP_MEMORY_REQUEST="${FLUENTD_GCP_MEMORY_REQUEST:-}" FLUENTD_GCP_MEMORY_REQUEST="${FLUENTD_GCP_MEMORY_REQUEST:-}"
...@@ -390,7 +390,7 @@ HEAPSTER_GCP_BASE_CPU="${HEAPSTER_GCP_BASE_CPU:-80m}" ...@@ -390,7 +390,7 @@ HEAPSTER_GCP_BASE_CPU="${HEAPSTER_GCP_BASE_CPU:-80m}"
HEAPSTER_GCP_CPU_PER_NODE="${HEAPSTER_GCP_CPU_PER_NODE:-0.5}" HEAPSTER_GCP_CPU_PER_NODE="${HEAPSTER_GCP_CPU_PER_NODE:-0.5}"
# Adding to PROVIDER_VARS, since this is GCP-specific. # Adding to PROVIDER_VARS, since this is GCP-specific.
PROVIDER_VARS="${PROVIDER_VARS:-} FLUENTD_GCP_VERSION FLUENTD_GCP_MEMORY_LIMIT FLUENTD_GCP_CPU_REQUEST FLUENTD_GCP_MEMORY_REQUEST HEAPSTER_GCP_BASE_MEMORY HEAPSTER_GCP_MEMORY_PER_NODE HEAPSTER_GCP_BASE_CPU HEAPSTER_GCP_CPU_PER_NODE CUSTOM_KUBE_DASHBOARD_BANNER" PROVIDER_VARS="${PROVIDER_VARS:-} FLUENTD_GCP_VERSION FLUENTD_GCP_MEMORY_LIMIT FLUENTD_GCP_CPU_REQUEST FLUENTD_GCP_MEMORY_REQUEST HEAPSTER_GCP_BASE_MEMORY HEAPSTER_GCP_MEMORY_PER_NODE HEAPSTER_GCP_BASE_CPU HEAPSTER_GCP_CPU_PER_NODE CUSTOM_KUBE_DASHBOARD_BANNER LOGGING_STACKDRIVER_RESOURCE_TYPES STACKDRIVER_METADATA_AGENT_URL"
# Fluentd configuration for node-journal # Fluentd configuration for node-journal
ENABLE_NODE_JOURNAL="${ENABLE_NODE_JOURNAL:-false}" ENABLE_NODE_JOURNAL="${ENABLE_NODE_JOURNAL:-false}"
......
...@@ -390,7 +390,7 @@ if [[ -n "${LOGROTATE_MAX_SIZE:-}" ]]; then ...@@ -390,7 +390,7 @@ if [[ -n "${LOGROTATE_MAX_SIZE:-}" ]]; then
fi fi
# Fluentd requirements # Fluentd requirements
FLUENTD_GCP_VERSION="${FLUENTD_GCP_VERSION:-0.2-1.5.28-1}" FLUENTD_GCP_VERSION="${FLUENTD_GCP_VERSION:-0.2-1.5.30-1-k8s}"
FLUENTD_GCP_MEMORY_LIMIT="${FLUENTD_GCP_MEMORY_LIMIT:-}" FLUENTD_GCP_MEMORY_LIMIT="${FLUENTD_GCP_MEMORY_LIMIT:-}"
FLUENTD_GCP_CPU_REQUEST="${FLUENTD_GCP_CPU_REQUEST:-}" FLUENTD_GCP_CPU_REQUEST="${FLUENTD_GCP_CPU_REQUEST:-}"
FLUENTD_GCP_MEMORY_REQUEST="${FLUENTD_GCP_MEMORY_REQUEST:-}" FLUENTD_GCP_MEMORY_REQUEST="${FLUENTD_GCP_MEMORY_REQUEST:-}"
...@@ -402,7 +402,7 @@ HEAPSTER_GCP_BASE_CPU="${HEAPSTER_GCP_BASE_CPU:-80m}" ...@@ -402,7 +402,7 @@ HEAPSTER_GCP_BASE_CPU="${HEAPSTER_GCP_BASE_CPU:-80m}"
HEAPSTER_GCP_CPU_PER_NODE="${HEAPSTER_GCP_CPU_PER_NODE:-0.5}" HEAPSTER_GCP_CPU_PER_NODE="${HEAPSTER_GCP_CPU_PER_NODE:-0.5}"
# Adding to PROVIDER_VARS, since this is GCP-specific. # Adding to PROVIDER_VARS, since this is GCP-specific.
PROVIDER_VARS="${PROVIDER_VARS:-} FLUENTD_GCP_VERSION FLUENTD_GCP_MEMORY_LIMIT FLUENTD_GCP_CPU_REQUEST FLUENTD_GCP_MEMORY_REQUEST HEAPSTER_GCP_BASE_MEMORY HEAPSTER_GCP_MEMORY_PER_NODE HEAPSTER_GCP_BASE_CPU HEAPSTER_GCP_CPU_PER_NODE CUSTOM_KUBE_DASHBOARD_BANNER" PROVIDER_VARS="${PROVIDER_VARS:-} FLUENTD_GCP_VERSION FLUENTD_GCP_MEMORY_LIMIT FLUENTD_GCP_CPU_REQUEST FLUENTD_GCP_MEMORY_REQUEST HEAPSTER_GCP_BASE_MEMORY HEAPSTER_GCP_MEMORY_PER_NODE HEAPSTER_GCP_BASE_CPU HEAPSTER_GCP_CPU_PER_NODE CUSTOM_KUBE_DASHBOARD_BANNER LOGGING_STACKDRIVER_RESOURCE_TYPES STACKDRIVER_METADATA_AGENT_URL"
# Fluentd configuration for node-journal # Fluentd configuration for node-journal
ENABLE_NODE_JOURNAL="${ENABLE_NODE_JOURNAL:-false}" ENABLE_NODE_JOURNAL="${ENABLE_NODE_JOURNAL:-false}"
......
...@@ -2051,6 +2051,36 @@ function setup-coredns-manifest { ...@@ -2051,6 +2051,36 @@ function setup-coredns-manifest {
sed -i -e "s@{{ *pillar\['service_cluster_ip_range'\] *}}@${SERVICE_CLUSTER_IP_RANGE}@g" "${coredns_file}" sed -i -e "s@{{ *pillar\['service_cluster_ip_range'\] *}}@${SERVICE_CLUSTER_IP_RANGE}@g" "${coredns_file}"
} }
# Sets up the manifests of Fluentd configmap and yamls for k8s addons.
function setup-fluentd {
local -r dst_dir="$1"
local -r fluentd_gcp_yaml="${dst_dir}/fluentd-gcp/fluentd-gcp-ds.yaml"
# Ingest logs against new resources like "k8s_container" and "k8s_node" if
# LOGGING_STACKDRIVER_RESOURCE_TYPES is "new".
# Ingest logs against old resources like "gke_container" and "gce_instance" if
# LOGGING_STACKDRIVER_RESOURCE_TYPES is "old".
if [[ "${LOGGING_STACKDRIVER_RESOURCE_TYPES:-old}" == "new" ]]; then
local -r fluentd_gcp_configmap_yaml="${dst_dir}/fluentd-gcp/fluentd-gcp-configmap.yaml"
fluentd_gcp_configmap_name="fluentd-gcp-config"
else
local -r fluentd_gcp_configmap_yaml="${dst_dir}/fluentd-gcp/fluentd-gcp-configmap-old.yaml"
fluentd_gcp_configmap_name="fluentd-gcp-config-old"
fi
sed -i -e "s@{{ fluentd_gcp_configmap_name }}@${fluentd_gcp_configmap_name}@g" "${fluentd_gcp_yaml}"
fluentd_gcp_version="${FLUENTD_GCP_VERSION:-0.2-1.5.30-1-k8s}"
sed -i -e "s@{{ fluentd_gcp_version }}@${fluentd_gcp_version}@g" "${fluentd_gcp_yaml}"
if [[ "${STACKDRIVER_METADATA_AGENT_URL:-}" != "" ]]; then
metadata_agent_url="${STACKDRIVER_METADATA_AGENT_URL}"
else
metadata_agent_url="http://${HOSTNAME}:8799"
fi
sed -i -e "s@{{ stackdriver_metadata_agent_url }}@${metadata_agent_url}@g" "${fluentd_gcp_yaml}"
update-prometheus-to-sd-parameters ${fluentd_gcp_yaml}
start-fluentd-resource-update ${fluentd_gcp_yaml}
update-container-runtime ${fluentd_gcp_configmap_yaml}
update-node-journal ${fluentd_gcp_configmap_yaml}
}
# Sets up the manifests of kube-dns for k8s addons. # Sets up the manifests of kube-dns for k8s addons.
function setup-kube-dns-manifest { function setup-kube-dns-manifest {
local -r kubedns_file="${dst_dir}/dns/kube-dns.yaml" local -r kubedns_file="${dst_dir}/dns/kube-dns.yaml"
...@@ -2187,17 +2217,10 @@ EOF ...@@ -2187,17 +2217,10 @@ EOF
if [[ "${ENABLE_NODE_LOGGING:-}" == "true" ]] && \ if [[ "${ENABLE_NODE_LOGGING:-}" == "true" ]] && \
[[ "${LOGGING_DESTINATION:-}" == "gcp" ]]; then [[ "${LOGGING_DESTINATION:-}" == "gcp" ]]; then
setup-addon-manifests "addons" "fluentd-gcp" setup-addon-manifests "addons" "fluentd-gcp"
setup-fluentd ${dst_dir}
local -r event_exporter_yaml="${dst_dir}/fluentd-gcp/event-exporter.yaml" local -r event_exporter_yaml="${dst_dir}/fluentd-gcp/event-exporter.yaml"
local -r fluentd_gcp_yaml="${dst_dir}/fluentd-gcp/fluentd-gcp-ds.yaml"
local -r fluentd_gcp_configmap_yaml="${dst_dir}/fluentd-gcp/fluentd-gcp-configmap.yaml"
update-event-exporter ${event_exporter_yaml} update-event-exporter ${event_exporter_yaml}
fluentd_gcp_version="${FLUENTD_GCP_VERSION:-0.2-1.5.28-1}"
sed -i -e "s@{{ fluentd_gcp_version }}@${fluentd_gcp_version}@g" "${fluentd_gcp_yaml}"
update-prometheus-to-sd-parameters ${event_exporter_yaml} update-prometheus-to-sd-parameters ${event_exporter_yaml}
update-prometheus-to-sd-parameters ${fluentd_gcp_yaml}
start-fluentd-resource-update ${fluentd_gcp_yaml}
update-container-runtime ${fluentd_gcp_configmap_yaml}
update-node-journal ${fluentd_gcp_configmap_yaml}
fi fi
if [[ "${ENABLE_CLUSTER_UI:-}" == "true" ]]; then if [[ "${ENABLE_CLUSTER_UI:-}" == "true" ]]; then
setup-addon-manifests "addons" "dashboard" setup-addon-manifests "addons" "dashboard"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment