The `cert-issuer-name` and `cert-issuer-kind` properties point an app's generated `Certificate` at a cert-manager issuer created outside of Dokku, allowing certificates to be issued through solvers the built-in letsencrypt integration cannot use, such as `dns01` for wildcard certificates. Setting an issuer enables https on its own, as a manually managed issuer has no email for Dokku to configure. An imported certificate still takes precedence, and `letsencrypt-server false` remains the single off switch. Dokku warns before a build starts when the referenced issuer is absent from the cluster, without blocking the deploy. Wildcard domains no longer collide with their apex domain when generating ingress names, and `letsencrypt-server` values are now validated when set rather than at deploy time.
799 lines
26 KiB
Go
799 lines
26 KiB
Go
package scheduler_k3s
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/dokku/dokku/plugins/common"
|
|
)
|
|
|
|
const (
|
|
annotationsReportType = "scheduler-k3s-annotations"
|
|
labelsReportType = "scheduler-k3s-labels"
|
|
autoscalingAuthReportType = "scheduler-k3s-autoscaling-auth"
|
|
|
|
// reportProcessTypeGlobal is the rendered form of GlobalProcessType in annotation
|
|
// and label report keys (both JSON and single-flag). Real process types cannot
|
|
// collide because they cannot contain "--".
|
|
reportProcessTypeGlobal = "global"
|
|
)
|
|
|
|
// tokenMask is shown in place of the raw token value in default stdout output.
|
|
const tokenMask = "*******"
|
|
|
|
// ReportSingleApp is an internal function that displays the scheduler-k3s report for one or more apps
|
|
func ReportSingleApp(appName string, format string, infoFlag string) error {
|
|
if appName != "--global" {
|
|
if err := common.VerifyAppName(appName); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
var flags map[string]common.ReportFunc
|
|
if appName == "--global" {
|
|
tokenFlag := "--scheduler-k3s-global-token"
|
|
tokenReportFunc := reportMaskedGlobalToken
|
|
if format == "json" || infoFlag == tokenFlag {
|
|
tokenReportFunc = reportGlobalToken
|
|
}
|
|
|
|
flags = map[string]common.ReportFunc{
|
|
"--scheduler-k3s-computed-cert-issuer-kind": reportComputedCertIssuerKind,
|
|
"--scheduler-k3s-global-cert-issuer-kind": reportGlobalCertIssuerKind,
|
|
"--scheduler-k3s-computed-cert-issuer-name": reportComputedCertIssuerName,
|
|
"--scheduler-k3s-global-cert-issuer-name": reportGlobalCertIssuerName,
|
|
"--scheduler-k3s-computed-deploy-timeout": reportComputedDeployTimeout,
|
|
"--scheduler-k3s-global-deploy-timeout": reportGlobalDeployTimeout,
|
|
"--scheduler-k3s-computed-image-pull-secrets": reportComputedImagePullSecrets,
|
|
"--scheduler-k3s-global-image-pull-secrets": reportGlobalImagePullSecrets,
|
|
"--scheduler-k3s-computed-ingress-class": reportComputedIngressClass,
|
|
"--scheduler-k3s-global-ingress-class": reportGlobalIngressClass,
|
|
"--scheduler-k3s-computed-kubeconfig-path": reportComputedKubeconfigPath,
|
|
"--scheduler-k3s-global-kubeconfig-path": reportGlobalKubeconfigPath,
|
|
"--scheduler-k3s-computed-kube-context": reportComputedKubeContext,
|
|
"--scheduler-k3s-global-kube-context": reportGlobalKubeContext,
|
|
"--scheduler-k3s-computed-kustomize-root-path": reportComputedKustomizeRootPath,
|
|
"--scheduler-k3s-global-kustomize-root-path": reportGlobalKustomizeRootPath,
|
|
"--scheduler-k3s-computed-letsencrypt-server": reportComputedLetsencryptServer,
|
|
"--scheduler-k3s-global-letsencrypt-server": reportGlobalLetsencryptServer,
|
|
"--scheduler-k3s-computed-letsencrypt-email-prod": reportComputedLetsencryptEmailProd,
|
|
"--scheduler-k3s-global-letsencrypt-email-prod": reportGlobalLetsencryptEmailProd,
|
|
"--scheduler-k3s-computed-letsencrypt-email-stag": reportComputedLetsencryptEmailStag,
|
|
"--scheduler-k3s-global-letsencrypt-email-stag": reportGlobalLetsencryptEmailStag,
|
|
"--scheduler-k3s-computed-namespace": reportComputedNamespace,
|
|
"--scheduler-k3s-global-namespace": reportGlobalNamespace,
|
|
"--scheduler-k3s-computed-network-interface": reportComputedNetworkInterface,
|
|
"--scheduler-k3s-global-network-interface": reportGlobalNetworkInterface,
|
|
"--scheduler-k3s-global-node-sysctls-image": reportGlobalNodeSysctlsImage,
|
|
"--scheduler-k3s-global-node-sysctls-pause-image": reportGlobalNodeSysctlsPauseImage,
|
|
"--scheduler-k3s-computed-rollback-on-failure": reportComputedRollbackOnFailure,
|
|
"--scheduler-k3s-global-rollback-on-failure": reportGlobalRollbackOnFailure,
|
|
"--scheduler-k3s-computed-shm-size": reportComputedShmSize,
|
|
"--scheduler-k3s-global-shm-size": reportGlobalShmSize,
|
|
tokenFlag: tokenReportFunc,
|
|
}
|
|
} else {
|
|
flags = map[string]common.ReportFunc{
|
|
"--scheduler-k3s-computed-cert-issuer-kind": reportComputedCertIssuerKind,
|
|
"--scheduler-k3s-cert-issuer-kind": reportCertIssuerKind,
|
|
"--scheduler-k3s-global-cert-issuer-kind": reportGlobalCertIssuerKind,
|
|
"--scheduler-k3s-computed-cert-issuer-name": reportComputedCertIssuerName,
|
|
"--scheduler-k3s-cert-issuer-name": reportCertIssuerName,
|
|
"--scheduler-k3s-global-cert-issuer-name": reportGlobalCertIssuerName,
|
|
"--scheduler-k3s-computed-deploy-timeout": reportComputedDeployTimeout,
|
|
"--scheduler-k3s-deploy-timeout": reportDeployTimeout,
|
|
"--scheduler-k3s-global-deploy-timeout": reportGlobalDeployTimeout,
|
|
"--scheduler-k3s-computed-image-pull-secrets": reportComputedImagePullSecrets,
|
|
"--scheduler-k3s-image-pull-secrets": reportImagePullSecrets,
|
|
"--scheduler-k3s-global-image-pull-secrets": reportGlobalImagePullSecrets,
|
|
"--scheduler-k3s-computed-ingress-class": reportComputedIngressClass,
|
|
"--scheduler-k3s-global-ingress-class": reportGlobalIngressClass,
|
|
"--scheduler-k3s-computed-kubeconfig-path": reportComputedKubeconfigPath,
|
|
"--scheduler-k3s-global-kubeconfig-path": reportGlobalKubeconfigPath,
|
|
"--scheduler-k3s-computed-kube-context": reportComputedKubeContext,
|
|
"--scheduler-k3s-global-kube-context": reportGlobalKubeContext,
|
|
"--scheduler-k3s-computed-kustomize-root-path": reportComputedKustomizeRootPath,
|
|
"--scheduler-k3s-kustomize-root-path": reportKustomizeRootPath,
|
|
"--scheduler-k3s-global-kustomize-root-path": reportGlobalKustomizeRootPath,
|
|
"--scheduler-k3s-computed-letsencrypt-server": reportComputedLetsencryptServer,
|
|
"--scheduler-k3s-letsencrypt-server": reportLetsencryptServer,
|
|
"--scheduler-k3s-global-letsencrypt-server": reportGlobalLetsencryptServer,
|
|
"--scheduler-k3s-computed-letsencrypt-email-prod": reportComputedLetsencryptEmailProd,
|
|
"--scheduler-k3s-letsencrypt-email-prod": reportLetsencryptEmailProd,
|
|
"--scheduler-k3s-global-letsencrypt-email-prod": reportGlobalLetsencryptEmailProd,
|
|
"--scheduler-k3s-computed-letsencrypt-email-stag": reportComputedLetsencryptEmailStag,
|
|
"--scheduler-k3s-letsencrypt-email-stag": reportLetsencryptEmailStag,
|
|
"--scheduler-k3s-global-letsencrypt-email-stag": reportGlobalLetsencryptEmailStag,
|
|
"--scheduler-k3s-computed-namespace": reportComputedNamespace,
|
|
"--scheduler-k3s-namespace": reportNamespace,
|
|
"--scheduler-k3s-global-namespace": reportGlobalNamespace,
|
|
"--scheduler-k3s-computed-network-interface": reportComputedNetworkInterface,
|
|
"--scheduler-k3s-global-network-interface": reportGlobalNetworkInterface,
|
|
"--scheduler-k3s-global-node-sysctls-image": reportGlobalNodeSysctlsImage,
|
|
"--scheduler-k3s-global-node-sysctls-pause-image": reportGlobalNodeSysctlsPauseImage,
|
|
"--scheduler-k3s-computed-rollback-on-failure": reportComputedRollbackOnFailure,
|
|
"--scheduler-k3s-rollback-on-failure": reportRollbackOnFailure,
|
|
"--scheduler-k3s-global-rollback-on-failure": reportGlobalRollbackOnFailure,
|
|
"--scheduler-k3s-computed-shm-size": reportComputedShmSize,
|
|
"--scheduler-k3s-global-shm-size": reportGlobalShmSize,
|
|
"--scheduler-k3s-shm-size": reportShmSize,
|
|
}
|
|
}
|
|
|
|
for _, chart := range HelmCharts {
|
|
chartOverrides, err := common.PropertyMapGet("scheduler-k3s", "--global", "chart-overrides."+chart.ReleaseName)
|
|
if err != nil {
|
|
return fmt.Errorf("Unable to get property list: %w", err)
|
|
}
|
|
for key, value := range chartOverrides {
|
|
flagName := fmt.Sprintf("--scheduler-k3s-global-chart.%s.%s", chart.ReleaseName, key)
|
|
value := value
|
|
flags[flagName] = func(appName string) string {
|
|
return value
|
|
}
|
|
}
|
|
}
|
|
|
|
flagKeys := []string{}
|
|
for flagKey := range flags {
|
|
flagKeys = append(flagKeys, flagKey)
|
|
}
|
|
|
|
infoFlags := common.CollectReport(appName, infoFlag, flags)
|
|
return common.ReportSingleApp(common.ReportSingleAppInput{
|
|
ReportType: "scheduler-k3s",
|
|
AppName: appName,
|
|
InfoFlag: infoFlag,
|
|
InfoFlags: infoFlags,
|
|
InfoFlagKeys: flagKeys,
|
|
Format: format,
|
|
TrimPrefix: true,
|
|
UppercaseFirstCharacter: true,
|
|
EmitLegacyPrefix: true,
|
|
})
|
|
}
|
|
|
|
// autoscalingAuthReportEntry is a parsed trigger authentication metadata entry.
|
|
type autoscalingAuthReportEntry struct {
|
|
Trigger string
|
|
MetadataKey string
|
|
Value string
|
|
}
|
|
|
|
// ReportAutoscalingAuthSingleApp displays the scheduler-k3s autoscaling-auth report for one app.
|
|
func ReportAutoscalingAuthSingleApp(appName string, format string, includeMetadata bool, infoFlag string) error {
|
|
if appName != "--global" {
|
|
if err := common.VerifyAppName(appName); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
entries, err := collectAutoscalingAuthEntries(appName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return renderAutoscalingAuthReport(autoscalingAuthRenderInput{
|
|
AppName: appName,
|
|
Entries: entries,
|
|
Format: format,
|
|
IncludeMetadata: includeMetadata,
|
|
InfoFlag: infoFlag,
|
|
})
|
|
}
|
|
|
|
// collectAutoscalingAuthEntries scans the property store for trigger authentication metadata.
|
|
func collectAutoscalingAuthEntries(appName string) ([]autoscalingAuthReportEntry, error) {
|
|
properties, err := common.PropertyGetAllByPrefix("scheduler-k3s", appName, TriggerAuthPropertyPrefix)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Unable to get property list: %w", err)
|
|
}
|
|
|
|
entries := []autoscalingAuthReportEntry{}
|
|
for key, value := range properties {
|
|
metadataKey := strings.TrimPrefix(key, TriggerAuthPropertyPrefix)
|
|
parts := strings.SplitN(metadataKey, ".", 2)
|
|
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
|
|
continue
|
|
}
|
|
|
|
entries = append(entries, autoscalingAuthReportEntry{
|
|
Trigger: parts[0],
|
|
MetadataKey: parts[1],
|
|
Value: value,
|
|
})
|
|
}
|
|
|
|
return entries, nil
|
|
}
|
|
|
|
type autoscalingAuthRenderInput struct {
|
|
AppName string
|
|
Entries []autoscalingAuthReportEntry
|
|
Format string
|
|
IncludeMetadata bool
|
|
InfoFlag string
|
|
}
|
|
|
|
// renderAutoscalingAuthReport dispatches to stdout/json/single-flag rendering for trigger auth.
|
|
func renderAutoscalingAuthReport(input autoscalingAuthRenderInput) error {
|
|
if input.Format != "stdout" && input.Format != "json" {
|
|
return fmt.Errorf("Invalid format: %s", input.Format)
|
|
}
|
|
if input.Format == "json" && input.InfoFlag != "" {
|
|
return fmt.Errorf("--format flag cannot be specified when specifying an info flag")
|
|
}
|
|
|
|
flatValues := map[string]string{}
|
|
flagToValue := map[string]string{}
|
|
flagPrefix := "--" + autoscalingAuthReportType + "."
|
|
for _, entry := range input.Entries {
|
|
composedKey := entry.Trigger + "." + entry.MetadataKey
|
|
flatValues[composedKey] = entry.Value
|
|
flagToValue[flagPrefix+composedKey] = entry.Value
|
|
}
|
|
|
|
if input.InfoFlag != "" {
|
|
value, ok := flagToValue[input.InfoFlag]
|
|
if !ok {
|
|
validFlags := []string{}
|
|
for flag := range flagToValue {
|
|
validFlags = append(validFlags, flag)
|
|
}
|
|
sort.Strings(validFlags)
|
|
return fmt.Errorf("Invalid flag passed, valid flags: %s", strings.Join(validFlags, ", "))
|
|
}
|
|
if value != "" {
|
|
fmt.Println(tokenMask)
|
|
} else {
|
|
fmt.Println("")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if input.Format == "json" {
|
|
b, err := json.Marshal(flatValues)
|
|
if err != nil {
|
|
return fmt.Errorf("Unable to marshal json: %w", err)
|
|
}
|
|
fmt.Println(string(b))
|
|
return nil
|
|
}
|
|
|
|
triggers := map[string]bool{}
|
|
for _, entry := range input.Entries {
|
|
triggers[entry.Trigger] = true
|
|
}
|
|
|
|
triggerKeys := []string{}
|
|
for trigger := range triggers {
|
|
triggerKeys = append(triggerKeys, trigger)
|
|
}
|
|
sort.Strings(triggerKeys)
|
|
|
|
common.LogInfo2Quiet(fmt.Sprintf("%s autoscaling-auth information", input.AppName))
|
|
if len(triggerKeys) == 0 {
|
|
return nil
|
|
}
|
|
|
|
length := 31
|
|
if input.IncludeMetadata {
|
|
for _, entry := range input.Entries {
|
|
label := fmt.Sprintf("%s %s:", common.UcFirst(entry.Trigger), entry.MetadataKey)
|
|
if len(label) > length {
|
|
length = len(label)
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, trigger := range triggerKeys {
|
|
label := fmt.Sprintf("%s:", common.UcFirst(trigger))
|
|
common.LogVerbose(fmt.Sprintf("%s%s", common.RightPad(label, length, " "), "configured"))
|
|
|
|
if !input.IncludeMetadata {
|
|
continue
|
|
}
|
|
|
|
triggerEntries := []autoscalingAuthReportEntry{}
|
|
for _, entry := range input.Entries {
|
|
if entry.Trigger == trigger {
|
|
triggerEntries = append(triggerEntries, entry)
|
|
}
|
|
}
|
|
sort.Slice(triggerEntries, func(i, j int) bool {
|
|
return triggerEntries[i].MetadataKey < triggerEntries[j].MetadataKey
|
|
})
|
|
|
|
for _, entry := range triggerEntries {
|
|
label := fmt.Sprintf("%s %s:", common.UcFirst(entry.Trigger), entry.MetadataKey)
|
|
common.LogVerbose(fmt.Sprintf("%s%s", common.RightPad(label, length, " "), entry.Value))
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ExtractReportInfoFlag scans arguments for a single info flag whose name starts with
|
|
// the given prefix (e.g. "--scheduler-k3s-annotations.") and returns it separately from
|
|
// the remaining passthrough arguments. Returns an error if more than one matching flag
|
|
// is present. Matching args are removed from the passthrough list so the caller's
|
|
// flag.FlagSet sees only its own flags and positional arguments.
|
|
func ExtractReportInfoFlag(prefix string, arguments []string) ([]string, string, error) {
|
|
passthrough := []string{}
|
|
infoFlag := ""
|
|
for _, argument := range arguments {
|
|
if strings.HasPrefix(argument, prefix) {
|
|
if infoFlag != "" {
|
|
return nil, "", fmt.Errorf("only a single info flag may be specified")
|
|
}
|
|
infoFlag = argument
|
|
continue
|
|
}
|
|
passthrough = append(passthrough, argument)
|
|
}
|
|
return passthrough, infoFlag, nil
|
|
}
|
|
|
|
// annotationsLabelsReportEntry is a parsed (processType, resourceType, key) triple
|
|
// produced when scanning the property store for annotations or labels.
|
|
type annotationsLabelsReportEntry struct {
|
|
ProcessType string
|
|
ResourceType string
|
|
Key string
|
|
Value string
|
|
}
|
|
|
|
// ReportAnnotationsSingleApp displays the scheduler-k3s annotations report for one app.
|
|
func ReportAnnotationsSingleApp(appName string, format string, processType string, resourceType string, infoFlag string) error {
|
|
if appName != "--global" {
|
|
if err := common.VerifyAppName(appName); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
entries, err := collectAnnotationsEntries(appName, processType, resourceType)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return renderAnnotationsLabelsReport(annotationsLabelsRenderInput{
|
|
AppName: appName,
|
|
ReportType: annotationsReportType,
|
|
Heading: "annotations",
|
|
RowLabel: "Annotation",
|
|
Entries: entries,
|
|
Format: format,
|
|
InfoFlag: infoFlag,
|
|
})
|
|
}
|
|
|
|
// ReportLabelsSingleApp displays the scheduler-k3s labels report for one app.
|
|
func ReportLabelsSingleApp(appName string, format string, processType string, resourceType string, infoFlag string) error {
|
|
if appName != "--global" {
|
|
if err := common.VerifyAppName(appName); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
entries, err := collectLabelsEntries(appName, processType, resourceType)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return renderAnnotationsLabelsReport(annotationsLabelsRenderInput{
|
|
AppName: appName,
|
|
ReportType: labelsReportType,
|
|
Heading: "labels",
|
|
RowLabel: "Label",
|
|
Entries: entries,
|
|
Format: format,
|
|
InfoFlag: infoFlag,
|
|
})
|
|
}
|
|
|
|
// collectAnnotationsEntries scans the property store for annotation entries on appName,
|
|
// optionally filtered by processType/resourceType.
|
|
func collectAnnotationsEntries(appName string, processType string, resourceType string) ([]annotationsLabelsReportEntry, error) {
|
|
properties, err := common.PropertyGetAllByPrefix("scheduler-k3s", appName, "")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Unable to get property list: %w", err)
|
|
}
|
|
|
|
knownResourceTypes := map[string]bool{}
|
|
for _, rt := range AnnotationResourceTypes {
|
|
knownResourceTypes[rt] = true
|
|
}
|
|
|
|
entries := []annotationsLabelsReportEntry{}
|
|
for propertyName := range properties {
|
|
if isReservedAnnotationProperty(propertyName) {
|
|
continue
|
|
}
|
|
|
|
dot := strings.LastIndex(propertyName, ".")
|
|
if dot <= 0 || dot == len(propertyName)-1 {
|
|
continue
|
|
}
|
|
|
|
propProcessType := propertyName[:dot]
|
|
propResourceType := propertyName[dot+1:]
|
|
if !knownResourceTypes[propResourceType] {
|
|
continue
|
|
}
|
|
|
|
if processType != "" && propProcessType != processType {
|
|
continue
|
|
}
|
|
if resourceType != "" && propResourceType != resourceType {
|
|
continue
|
|
}
|
|
|
|
annotationMap, err := getAnnotation(appName, propProcessType, propResourceType)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Unable to read annotation %s: %w", propertyName, err)
|
|
}
|
|
|
|
for key, value := range annotationMap {
|
|
entries = append(entries, annotationsLabelsReportEntry{
|
|
ProcessType: propProcessType,
|
|
ResourceType: propResourceType,
|
|
Key: key,
|
|
Value: value,
|
|
})
|
|
}
|
|
}
|
|
|
|
return entries, nil
|
|
}
|
|
|
|
// collectLabelsEntries scans the property store for label entries on appName,
|
|
// optionally filtered by processType/resourceType.
|
|
func collectLabelsEntries(appName string, processType string, resourceType string) ([]annotationsLabelsReportEntry, error) {
|
|
properties, err := common.PropertyGetAllByPrefix("scheduler-k3s", appName, "labels.")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Unable to get property list: %w", err)
|
|
}
|
|
|
|
knownResourceTypes := map[string]bool{}
|
|
for _, rt := range LabelResourceTypes {
|
|
knownResourceTypes[rt] = true
|
|
}
|
|
|
|
entries := []annotationsLabelsReportEntry{}
|
|
for propertyName := range properties {
|
|
suffix := strings.TrimPrefix(propertyName, "labels.")
|
|
dot := strings.LastIndex(suffix, ".")
|
|
if dot <= 0 || dot == len(suffix)-1 {
|
|
continue
|
|
}
|
|
|
|
propProcessType := suffix[:dot]
|
|
propResourceType := suffix[dot+1:]
|
|
if !knownResourceTypes[propResourceType] {
|
|
continue
|
|
}
|
|
|
|
if processType != "" && propProcessType != processType {
|
|
continue
|
|
}
|
|
if resourceType != "" && propResourceType != resourceType {
|
|
continue
|
|
}
|
|
|
|
labelMap, err := getLabel(appName, propProcessType, propResourceType)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Unable to read label %s: %w", propertyName, err)
|
|
}
|
|
|
|
for key, value := range labelMap {
|
|
entries = append(entries, annotationsLabelsReportEntry{
|
|
ProcessType: propProcessType,
|
|
ResourceType: propResourceType,
|
|
Key: key,
|
|
Value: value,
|
|
})
|
|
}
|
|
}
|
|
|
|
return entries, nil
|
|
}
|
|
|
|
func isReservedAnnotationProperty(propertyName string) bool {
|
|
for _, prefix := range reservedAnnotationPrefixes {
|
|
if strings.HasPrefix(propertyName, prefix) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
type annotationsLabelsRenderInput struct {
|
|
AppName string
|
|
ReportType string
|
|
// Heading is the plural noun used in stdout headers (e.g. "annotations", "labels").
|
|
Heading string
|
|
// RowLabel is the singular noun used to prefix each stdout row (e.g. "Annotation").
|
|
RowLabel string
|
|
Entries []annotationsLabelsReportEntry
|
|
Format string
|
|
InfoFlag string
|
|
}
|
|
|
|
// renderAnnotationsLabelsReport dispatches to stdout/json/single-flag rendering for the
|
|
// annotations and labels reports.
|
|
func renderAnnotationsLabelsReport(input annotationsLabelsRenderInput) error {
|
|
if input.Format != "stdout" && input.Format != "json" {
|
|
return fmt.Errorf("Invalid format: %s", input.Format)
|
|
}
|
|
if input.Format == "json" && input.InfoFlag != "" {
|
|
return fmt.Errorf("--format flag cannot be specified when specifying an info flag")
|
|
}
|
|
|
|
flatValues := map[string]string{}
|
|
flagToValue := map[string]string{}
|
|
flagPrefix := "--" + input.ReportType + "."
|
|
for _, entry := range input.Entries {
|
|
composedKey := renderProcessType(entry.ProcessType) + "." + entry.ResourceType + "." + entry.Key
|
|
flatValues[composedKey] = entry.Value
|
|
flagToValue[flagPrefix+composedKey] = entry.Value
|
|
}
|
|
|
|
if input.InfoFlag != "" {
|
|
value, ok := flagToValue[input.InfoFlag]
|
|
if !ok {
|
|
validFlags := []string{}
|
|
for flag := range flagToValue {
|
|
validFlags = append(validFlags, flag)
|
|
}
|
|
sort.Strings(validFlags)
|
|
return fmt.Errorf("Invalid flag passed, valid flags: %s", strings.Join(validFlags, ", "))
|
|
}
|
|
fmt.Println(value)
|
|
return nil
|
|
}
|
|
|
|
if input.Format == "json" {
|
|
b, err := json.Marshal(flatValues)
|
|
if err != nil {
|
|
return fmt.Errorf("Unable to marshal json: %w", err)
|
|
}
|
|
fmt.Println(string(b))
|
|
return nil
|
|
}
|
|
|
|
entriesByGroup := map[string][]annotationsLabelsReportEntry{}
|
|
for _, entry := range input.Entries {
|
|
groupKey := renderProcessType(entry.ProcessType) + "/" + entry.ResourceType
|
|
entriesByGroup[groupKey] = append(entriesByGroup[groupKey], entry)
|
|
}
|
|
|
|
groupKeys := []string{}
|
|
for groupKey := range entriesByGroup {
|
|
groupKeys = append(groupKeys, groupKey)
|
|
}
|
|
sort.Strings(groupKeys)
|
|
|
|
common.LogInfo2Quiet(fmt.Sprintf("%s %s information", input.AppName, input.Heading))
|
|
if len(groupKeys) == 0 {
|
|
return nil
|
|
}
|
|
|
|
for _, groupKey := range groupKeys {
|
|
groupEntries := entriesByGroup[groupKey]
|
|
sort.Slice(groupEntries, func(i, j int) bool {
|
|
return groupEntries[i].Key < groupEntries[j].Key
|
|
})
|
|
|
|
length := 31
|
|
for _, entry := range groupEntries {
|
|
label := fmt.Sprintf("%s (%s) %s:", input.RowLabel, groupKey, entry.Key)
|
|
if len(label) > length {
|
|
length = len(label)
|
|
}
|
|
}
|
|
|
|
for _, entry := range groupEntries {
|
|
label := fmt.Sprintf("%s (%s) %s:", input.RowLabel, groupKey, entry.Key)
|
|
common.LogVerbose(fmt.Sprintf("%s%s", common.RightPad(label, length, " "), entry.Value))
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// renderProcessType normalizes the in-storage process type for display in JSON keys,
|
|
// stdout headers, and single-flag lookups. GlobalProcessType (literal "--global") is
|
|
// rendered as "global"; real process types pass through unchanged.
|
|
func renderProcessType(processType string) string {
|
|
if processType == GlobalProcessType {
|
|
return reportProcessTypeGlobal
|
|
}
|
|
return processType
|
|
}
|
|
|
|
func reportComputedDeployTimeout(appName string) string {
|
|
return getComputedDeployTimeout(appName)
|
|
}
|
|
|
|
func reportDeployTimeout(appName string) string {
|
|
return getDeployTimeout(appName)
|
|
}
|
|
|
|
func reportGlobalDeployTimeout(appName string) string {
|
|
return getGlobalDeployTimeout()
|
|
}
|
|
|
|
func reportComputedImagePullSecrets(appName string) string {
|
|
return getComputedImagePullSecrets(appName)
|
|
}
|
|
|
|
func reportImagePullSecrets(appName string) string {
|
|
return getImagePullSecrets(appName)
|
|
}
|
|
|
|
func reportGlobalImagePullSecrets(appName string) string {
|
|
return getGlobalImagePullSecrets()
|
|
}
|
|
|
|
func reportComputedIngressClass(appName string) string {
|
|
return getComputedIngressClass()
|
|
}
|
|
|
|
func reportGlobalIngressClass(appName string) string {
|
|
return getGlobalIngressClass()
|
|
}
|
|
|
|
func reportComputedKubeconfigPath(appName string) string {
|
|
return getComputedKubeconfigPath()
|
|
}
|
|
|
|
func reportGlobalKubeconfigPath(appName string) string {
|
|
return getGlobalKubeconfigPath()
|
|
}
|
|
|
|
func reportComputedKubeContext(appName string) string {
|
|
return getComputedKubeContext()
|
|
}
|
|
|
|
func reportGlobalKubeContext(appName string) string {
|
|
return getGlobalKubeContext()
|
|
}
|
|
|
|
func reportComputedCertIssuerKind(appName string) string {
|
|
return getComputedCertIssuerKind(appName)
|
|
}
|
|
|
|
func reportCertIssuerKind(appName string) string {
|
|
return getCertIssuerKind(appName)
|
|
}
|
|
|
|
func reportGlobalCertIssuerKind(appName string) string {
|
|
return getGlobalCertIssuerKind()
|
|
}
|
|
|
|
func reportComputedCertIssuerName(appName string) string {
|
|
return getComputedCertIssuerName(appName)
|
|
}
|
|
|
|
func reportCertIssuerName(appName string) string {
|
|
return getCertIssuerName(appName)
|
|
}
|
|
|
|
func reportGlobalCertIssuerName(appName string) string {
|
|
return getGlobalCertIssuerName()
|
|
}
|
|
|
|
func reportComputedLetsencryptServer(appName string) string {
|
|
return getComputedLetsencryptServer(appName)
|
|
}
|
|
|
|
func reportLetsencryptServer(appName string) string {
|
|
return getLetsencryptServer(appName)
|
|
}
|
|
|
|
func reportGlobalLetsencryptServer(appName string) string {
|
|
return getGlobalLetsencryptServer()
|
|
}
|
|
|
|
func reportComputedLetsencryptEmailProd(appName string) string {
|
|
return getComputedLetsencryptEmailProd(appName)
|
|
}
|
|
|
|
func reportLetsencryptEmailProd(appName string) string {
|
|
return getLetsencryptEmailProd(appName)
|
|
}
|
|
|
|
func reportGlobalLetsencryptEmailProd(appName string) string {
|
|
return getGlobalLetsencryptEmailProd()
|
|
}
|
|
|
|
func reportComputedLetsencryptEmailStag(appName string) string {
|
|
return getComputedLetsencryptEmailStag(appName)
|
|
}
|
|
|
|
func reportLetsencryptEmailStag(appName string) string {
|
|
return getLetsencryptEmailStag(appName)
|
|
}
|
|
|
|
func reportGlobalLetsencryptEmailStag(appName string) string {
|
|
return getGlobalLetsencryptEmailStag()
|
|
}
|
|
|
|
func reportComputedKustomizeRootPath(appName string) string {
|
|
return getComputedKustomizeRootPath(appName)
|
|
}
|
|
|
|
func reportKustomizeRootPath(appName string) string {
|
|
return getKustomizeRootPath(appName)
|
|
}
|
|
|
|
func reportGlobalKustomizeRootPath(appName string) string {
|
|
return getGlobalKustomizeRootPath()
|
|
}
|
|
|
|
func reportComputedNamespace(appName string) string {
|
|
return getComputedNamespace(appName)
|
|
}
|
|
|
|
func reportNamespace(appName string) string {
|
|
return getNamespace(appName)
|
|
}
|
|
|
|
func reportGlobalNamespace(appName string) string {
|
|
return getGlobalNamespace()
|
|
}
|
|
|
|
func reportComputedNetworkInterface(appName string) string {
|
|
return getComputedNetworkInterface()
|
|
}
|
|
|
|
func reportGlobalNetworkInterface(appName string) string {
|
|
return getGlobalNetworkInterface()
|
|
}
|
|
|
|
func reportGlobalNodeSysctlsImage(appName string) string {
|
|
return getComputedNodeSysctlsImage()
|
|
}
|
|
|
|
func reportGlobalNodeSysctlsPauseImage(appName string) string {
|
|
return getComputedNodeSysctlsPauseImage()
|
|
}
|
|
|
|
func reportComputedRollbackOnFailure(appName string) string {
|
|
return getComputedRollbackOnFailure(appName)
|
|
}
|
|
|
|
func reportRollbackOnFailure(appName string) string {
|
|
return getRollbackOnFailure(appName)
|
|
}
|
|
|
|
func reportGlobalRollbackOnFailure(appName string) string {
|
|
return getGlobalRollbackOnFailure()
|
|
}
|
|
|
|
func reportComputedShmSize(appName string) string {
|
|
return getComputedShmSize(appName)
|
|
}
|
|
|
|
func reportGlobalShmSize(appName string) string {
|
|
return getGlobalShmSize()
|
|
}
|
|
|
|
func reportShmSize(appName string) string {
|
|
return getShmSize(appName)
|
|
}
|
|
|
|
func reportGlobalToken(appName string) string {
|
|
return getGlobalGlobalToken()
|
|
}
|
|
|
|
func reportMaskedGlobalToken(appName string) string {
|
|
value := getGlobalGlobalToken()
|
|
if value == "" {
|
|
return ""
|
|
}
|
|
return tokenMask
|
|
}
|