Skip to content

Commit

Permalink
set consul server locality from k8s node labels
Browse files Browse the repository at this point in the history
  • Loading branch information
erichaberkorn committed Apr 27, 2023
1 parent f8eb931 commit 9532ad4
Show file tree
Hide file tree
Showing 9 changed files with 332 additions and 7 deletions.
3 changes: 3 additions & 0 deletions .changelog/2093.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
control-plane: set agent localities on Consul servers to the server node's `topology.kubernetes.io/region` label.
```
4 changes: 2 additions & 2 deletions charts/consul/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ Consul server environment variables for consul-k8s commands.
{{- end }}
{{- if and .Values.externalServers.enabled .Values.externalServers.skipServerWatch }}
- name: CONSUL_SKIP_SERVER_WATCH
value: "true"
value: "true"
{{- end }}
{{- end -}}

Expand Down Expand Up @@ -366,7 +366,7 @@ Usage: {{ template "consul.validateCloudSecretKeys" . }}

*/}}
{{- define "consul.validateCloudSecretKeys" -}}
{{- if and .Values.global.cloud.enabled }}
{{- if and .Values.global.cloud.enabled }}
{{- if or (and .Values.global.cloud.resourceId.secretName (not .Values.global.cloud.resourceId.secretKey)) (and .Values.global.cloud.resourceId.secretKey (not .Values.global.cloud.resourceId.secretName)) }}
{{fail "When either global.cloud.resourceId.secretName or global.cloud.resourceId.secretKey is defined, both must be set."}}
{{- end }}
Expand Down
1 change: 1 addition & 0 deletions charts/consul/templates/client-daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ spec:
-recursor={{ quote $value }} \
{{- end }}
-config-file=/consul/extra-config/extra-from-values.json \
-config-file=/consul/extra-config/locality.json \
-domain={{ .Values.global.domain }}
volumeMounts:
- name: data
Expand Down
16 changes: 16 additions & 0 deletions charts/consul/templates/server-clusterrole.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ template "consul.fullname" . }}-server
namespace: {{ .Release.Namespace }}
labels:
app: {{ template "consul.name" . }}
chart: {{ template "consul.chart" . }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
component: server
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs:
- get
18 changes: 18 additions & 0 deletions charts/consul/templates/server-clusterrolebinding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ template "consul.fullname" . }}-server
labels:
app: {{ template "consul.name" . }}
chart: {{ template "consul.chart" . }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
component: server
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ template "consul.fullname" . }}-server
subjects:
- kind: ServiceAccount
name: {{ template "consul.fullname" . }}-server
namespace: {{ .Release.Namespace }}
27 changes: 22 additions & 5 deletions charts/consul/templates/server-statefulset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,22 @@ spec:
{{- if .Values.server.priorityClassName }}
priorityClassName: {{ .Values.server.priorityClassName | quote }}
{{- end }}
initContainers:
- name: locality-init
image: {{ .Values.global.imageK8S }}
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
command:
- "/bin/sh"
- "-ec"
- |
consul-k8s-control-plane fetch-server-region -node-name "$NODE_NAME" -output-file /consul/extra-config/locality.json
volumeMounts:
- name: extra-config
mountPath: /consul/extra-config
containers:
- name: consul
image: "{{ default .Values.global.image .Values.server.image }}"
Expand Down Expand Up @@ -291,9 +307,9 @@ spec:
{{- end }}
{{- if .Values.global.cloud.enabled}}
# These are mounted as secrets so that the consul server agent can use them.
# - the hcp-go-sdk in consul agent will already look for HCP_CLIENT_ID, HCP_CLIENT_SECRET, HCP_AUTH_URL,
# - the hcp-go-sdk in consul agent will already look for HCP_CLIENT_ID, HCP_CLIENT_SECRET, HCP_AUTH_URL,
# HCP_SCADA_ADDRESS, and HCP_API_HOST. so nothing more needs to be done.
# - HCP_RESOURCE_ID is created for use in the
# - HCP_RESOURCE_ID is created for use in the
# `-hcl="cloud { resource_id = \"${HCP_RESOURCE_ID}\" }"` logic in the command below.
{{- if .Values.global.cloud.clientId.secretName }}
- name: HCP_CLIENT_ID
Expand Down Expand Up @@ -328,15 +344,15 @@ spec:
valueFrom:
secretKeyRef:
name: {{ .Values.global.cloud.apiHost.secretName }}
key: {{ .Values.global.cloud.apiHost.secretKey }}
key: {{ .Values.global.cloud.apiHost.secretKey }}
{{- end}}
{{- if .Values.global.cloud.scadaAddress.secretName }}
- name: HCP_SCADA_ADDRESS
valueFrom:
secretKeyRef:
name: {{ .Values.global.cloud.scadaAddress.secretName }}
key: {{ .Values.global.cloud.scadaAddress.secretKey }}
{{- end}}
{{- end}}
{{- end }}
{{- include "consul.extraEnvironmentVars" .Values.server | nindent 12 }}
command:
Expand Down Expand Up @@ -375,7 +391,8 @@ spec:
-config-dir=/consul/userconfig/{{ .name }} \
{{- end }}
{{- end }}
-config-file=/consul/extra-config/extra-from-values.json
-config-file=/consul/extra-config/extra-from-values.json \
-config-file=/consul/extra-config/locality.json
{{- if and .Values.global.cloud.enabled .Values.global.cloud.resourceId.secretName }}
-hcl="cloud { resource_id = \"${HCP_RESOURCE_ID}\" }"
{{- end }}
Expand Down
4 changes: 4 additions & 0 deletions control-plane/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
cmdConsulLogout "github.com/hashicorp/consul-k8s/control-plane/subcommand/consul-logout"
cmdCreateFederationSecret "github.com/hashicorp/consul-k8s/control-plane/subcommand/create-federation-secret"
cmdDeleteCompletedJob "github.com/hashicorp/consul-k8s/control-plane/subcommand/delete-completed-job"
cmdFetchServerRegion "github.com/hashicorp/consul-k8s/control-plane/subcommand/fetch-server-region"
cmdGetConsulClientCA "github.com/hashicorp/consul-k8s/control-plane/subcommand/get-consul-client-ca"
cmdGossipEncryptionAutogenerate "github.com/hashicorp/consul-k8s/control-plane/subcommand/gossip-encryption-autogenerate"
cmdInjectConnect "github.com/hashicorp/consul-k8s/control-plane/subcommand/inject-connect"
Expand Down Expand Up @@ -90,6 +91,9 @@ func init() {
"install-cni": func() (cli.Command, error) {
return &cmdInstallCNI.Command{UI: ui}, nil
},
"fetch-server-region": func() (cli.Command, error) {
return &cmdFetchServerRegion.Command{UI: ui}, nil
},
}
}

Expand Down
158 changes: 158 additions & 0 deletions control-plane/subcommand/fetch-server-region/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package fetchserverregion

import (
"context"
"encoding/json"
"flag"
"fmt"
"os"
"sync"

"github.com/hashicorp/consul-k8s/control-plane/subcommand/common"
"github.com/hashicorp/consul-k8s/control-plane/subcommand/flags"
"github.com/hashicorp/go-hclog"
"github.com/mitchellh/cli"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)

// The consul-logout command issues a Consul logout API request to delete an ACL token.
type Command struct {
UI cli.Ui

flagLogLevel string
flagLogJSON bool
flagNodeName string
flagOutputFile string

flagSet *flag.FlagSet
k8s *flags.K8SFlags

once sync.Once
help string
logger hclog.Logger

// for testing
clientset kubernetes.Interface
}

type Locality struct {
Region string `json:"region"`
}

type Config struct {
Locality Locality `json:"locality"`
}

func (c *Command) init() {
c.flagSet = flag.NewFlagSet("", flag.ContinueOnError)
c.flagSet.StringVar(&c.flagLogLevel, "log-level", "info",
"Log verbosity level. Supported values (in order of detail) are \"trace\", "+
"\"debug\", \"info\", \"warn\", and \"error\".")
c.flagSet.BoolVar(&c.flagLogJSON, "log-json", false,
"Enable or disable JSON output format for logging.")
c.flagSet.StringVar(&c.flagNodeName, "node-name", "",
"Specifies the node name that will be used.")
c.flagSet.StringVar(&c.flagOutputFile, "output-file", "",
"The file path for writing the locality portion of a Consul agent configuration to.")

c.k8s = &flags.K8SFlags{}
flags.Merge(c.flagSet, c.k8s.Flags())

c.help = flags.Usage(help, c.flagSet)

}

func (c *Command) Run(args []string) int {
var err error
c.once.Do(c.init)

if err := c.flagSet.Parse(args); err != nil {
return 1
}

if c.logger == nil {
c.logger, err = common.Logger(c.flagLogLevel, c.flagLogJSON)
if err != nil {
c.UI.Error(err.Error())
return 1
}
}

if c.clientset == nil {
config, err := rest.InClusterConfig()
if err != nil {
// This just allows us to test it locally.
kubeconfig := clientcmd.RecommendedHomeFile
config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
c.UI.Error(err.Error())
return 1
}
}

c.clientset, err = kubernetes.NewForConfig(config)
if err != nil {
c.UI.Error(err.Error())
return 1
}
}

if c.flagNodeName == "" {
c.UI.Error("-node-name is required")
return 1
}

if c.flagOutputFile == "" {
c.UI.Error("-output-file is required")
return 1
}

config := c.fetchLocalityConfig()

jsonData, err := json.Marshal(config)
if err != nil {
c.UI.Error(err.Error())
return 1
}

err = os.WriteFile(c.flagOutputFile, jsonData, 0644)
if err != nil {
c.UI.Error(fmt.Sprintf("Error writing locality file: %s", err))
return 1
}

return 0
}

func (c *Command) fetchLocalityConfig() Config {
var cfg Config
node, err := c.clientset.CoreV1().Nodes().Get(context.Background(), c.flagNodeName, metav1.GetOptions{})
if err != nil {
return cfg
}

cfg.Locality.Region = node.Labels[corev1.LabelTopologyRegion]

return cfg
}

func (c *Command) Synopsis() string { return synopsis }
func (c *Command) Help() string {
c.once.Do(c.init)
return c.help
}

const synopsis = "Fetch the cloud region for a Consul server from the Kubernetes node's region label."
const help = `
Usage: consul-k8s-control-plane fetch-server-region [options]
Fetch the region for a Consul server.
Not intended for stand-alone use.
`
Loading

0 comments on commit 9532ad4

Please sign in to comment.