Skip to content
This repository has been archived by the owner on Oct 12, 2023. It is now read-only.

Liveness probe #309

Merged
merged 13 commits into from
Jul 25, 2019
11 changes: 11 additions & 0 deletions cmd/mic/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

"github.com/Azure/aad-pod-identity/pkg/mic"
"github.com/Azure/aad-pod-identity/pkg/probes"
"github.com/Azure/aad-pod-identity/version"
"github.com/golang/glog"
"k8s.io/client-go/rest"
Expand All @@ -19,6 +20,7 @@ var (
versionInfo bool
syncRetryDuration time.Duration
leaderElectionCfg mic.LeaderElectionConfig
httpProbePort string
)

func main() {
Expand All @@ -39,6 +41,9 @@ func main() {
flag.StringVar(&leaderElectionCfg.Name, "leader-election-name", "aad-pod-identity-mic", "leader election name")
flag.DurationVar(&leaderElectionCfg.Duration, "leader-election-duration", time.Second*15, "leader election duration")

//Probe port
flag.StringVar(&httpProbePort, "http-probe-port", "8080", "http liveliness probe port")

flag.Parse()
if versionInfo {
version.PrintVersionAndExit()
Expand All @@ -63,6 +68,12 @@ func main() {
if err != nil {
glog.Fatalf("Could not get the MIC client: %+v", err)
}

// Health probe will always report success once its started.
// MIC instance will report the contents as "Active" only once its elected the leader
// and starts the sync loop.
probes.InitAndStart(httpProbePort, &micClient.SyncLoopStarted)

// Starts the leader election loop
micClient.Run()
glog.Info("AAD Pod identity controller initialized!!")
Expand Down
6 changes: 6 additions & 0 deletions cmd/nmi/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/Azure/aad-pod-identity/pkg/k8s"
server "github.com/Azure/aad-pod-identity/pkg/nmi/server"
"github.com/Azure/aad-pod-identity/pkg/probes"
"github.com/Azure/aad-pod-identity/version"
log "github.com/sirupsen/logrus"
"github.com/spf13/pflag"
Expand All @@ -28,6 +29,7 @@ var (
ipTableUpdateTimeIntervalInSeconds = pflag.Int("ipt-update-interval-sec", defaultIPTableUpdateTimeIntervalInSeconds, "update interval of iptables")
forceNamespaced = pflag.Bool("forceNamespaced", false, "Forces mic to namespace identities, binding, and assignment")
micNamespace = pflag.String("MICNamespace", "default", "MIC namespace to short circuit MIC token requests")
httpProbePort = pflag.String("http-probe-port", "8080", "Http health and liveness probe port")
)

func main() {
Expand All @@ -53,6 +55,10 @@ func main() {
s.NodeName = *nodename
s.IPTableUpdateTimeIntervalInSeconds = *ipTableUpdateTimeIntervalInSeconds

// Health probe will always report success once its started. The contents
// will report "Active" once the iptables rules are set
probes.InitAndStart(*httpProbePort, &s.Initialized)

if err := s.Run(); err != nil {
log.Fatalf("%s", err)
}
Expand Down
12 changes: 12 additions & 0 deletions deploy/infra/master/replicaset/deployment-rbac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ spec:
volumeMounts:
- mountPath: /run/xtables.lock
name: iptableslock
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
nodeSelector:
beta.kubernetes.io/os: linux
---
Expand Down Expand Up @@ -200,6 +206,12 @@ spec:
- name: k8s-azure-file
mountPath: /etc/kubernetes/azure.json
readOnly: true
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
volumes:
- name: k8s-azure-file
hostPath:
Expand Down
12 changes: 12 additions & 0 deletions deploy/infra/master/replicaset/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ spec:
volumeMounts:
- mountPath: /run/xtables.lock
name: iptableslock
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
nodeSelector:
beta.kubernetes.io/os: linux
---
Expand Down Expand Up @@ -116,6 +122,12 @@ spec:
- name: k8s-azure-file
mountPath: /etc/kubernetes/azure.json
readOnly: true
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
volumes:
- name: kubeconfig
hostPath:
Expand Down
4 changes: 3 additions & 1 deletion pkg/mic/mic.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type Client struct {
EventChannel chan aadpodid.EventType
NodeClient NodeGetter
IsNamespaced bool
SyncLoopStarted bool
syncRetryInterval time.Duration

syncing int32 // protect against conucrrent sync's
Expand Down Expand Up @@ -125,7 +126,7 @@ func NewMICClient(cloudconfig string, config *rest.Config, isNamespaced bool, sy

// Run - Initiates the leader election run call to find if its leader and run it
func (c *Client) Run() {
glog.Infof("MIC Leader election initiated")
glog.Infof("Initiating MIC Leader election")
c.leaderElector.Run()
}

Expand Down Expand Up @@ -217,6 +218,7 @@ func (c *Client) Sync(exit <-chan struct{}) {
defer ticker.Stop()

glog.Info("Sync thread started.")
c.SyncLoopStarted = true
var event aadpodid.EventType
for {
select {
Expand Down
25 changes: 18 additions & 7 deletions pkg/nmi/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type Server struct {
IPTableUpdateTimeIntervalInSeconds int
IsNamespaced bool
MICNamespace string
Initialized bool
}

// NMIResponse is the response returned to caller
Expand Down Expand Up @@ -80,6 +81,17 @@ func (s *Server) Run() error {
return nil
}

func (s *Server) updateIPTableRulesInternal() {
log.Infof("node(%s) hostip(%s) metadataaddress(%s:%s) nmiport(%s)", s.NodeName, s.HostIP, s.MetadataIP, s.MetadataPort, s.NMIPort)

if err := iptables.AddCustomChain(s.MetadataIP, s.MetadataPort, s.HostIP, s.NMIPort); err != nil {
log.Fatalf("%s", err)
}
if err := iptables.LogCustomChain(); err != nil {
log.Fatalf("%s", err)
}
}

// updateIPTableRules ensures the correct iptable rules are set
// such that metadata requests are received by nmi assigned port
// NOT originating from HostIP destined to metadata endpoint are
Expand All @@ -91,6 +103,11 @@ func (s *Server) updateIPTableRules() {
ticker := time.NewTicker(time.Second * time.Duration(s.IPTableUpdateTimeIntervalInSeconds))
defer ticker.Stop()

// Run once before the waiting on ticker for the rules to take effect
// immediately.
s.updateIPTableRulesInternal()
s.Initialized = true

loop:
for {
select {
Expand All @@ -99,13 +116,7 @@ loop:
break loop

case <-ticker.C:
log.Infof("node(%s) hostip(%s) metadataaddress(%s:%s) nmiport(%s)", s.NodeName, s.HostIP, s.MetadataIP, s.MetadataPort, s.NMIPort)
if err := iptables.AddCustomChain(s.MetadataIP, s.MetadataPort, s.HostIP, s.NMIPort); err != nil {
log.Fatalf("%s", err)
}
if err := iptables.LogCustomChain(); err != nil {
log.Fatalf("%s", err)
}
s.updateIPTableRulesInternal()
}
}
}
Expand Down
45 changes: 45 additions & 0 deletions pkg/probes/probes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package probes

import (
"net/http"

"github.com/golang/glog"
)

// InitHealthProbe - sets up a health probe which responds with success (200 - OK) once its initialized.
// The contents of the healthz endpoint will be the string "Active" if the condition is satisfied.
// The condition is set to true when the sync cycle has become active in case of MIC and the iptables
// rules set in case of NMI.
func InitHealthProbe(condition *bool) {
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
if *condition {
w.Write([]byte("Active"))
} else {
w.Write([]byte("Not Active"))
}
})
}

func startAsync(port string) {
err := http.ListenAndServe(":"+port, nil)
if err != nil {
glog.Fatalf("Http listen and serve error: %+v", err)
} else {
glog.V(1).Infof("Http listen and serve started !")
}
}

//Start - Starts the required http server to start the probe to respond.
func Start(port string) {
go startAsync(port)
}

// InitAndStart - Initialize the default probes and starts the http listening port.
func InitAndStart(port string, condition *bool) {
InitHealthProbe(condition)
glog.V(1).Infof("Initialized health probe")
// start the probe.
Start(port)
glog.Infof("Initialized and started health probe")
}
12 changes: 9 additions & 3 deletions test/common/k8s/infra/infra.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,22 @@ import (
)

// CreateInfra will deploy all the infrastructure components (nmi and mic) on a Kubernetes cluster
func CreateInfra(namespace, registry, nmiVersion, micVersion, templateOutputPath string) error {
t, err := template.New("deployment-rbac.yaml").ParseFiles(path.Join("template", "deployment-rbac.yaml"))
func CreateInfra(namespace, registry, nmiVersion, micVersion, templateOutputPath string, old bool) error {
var err error
var t *template.Template
if !old {
t, err = template.New("deployment-rbac.yaml").ParseFiles(path.Join("template", "deployment-rbac.yaml"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than having 2 different deployment files, we should just template the single file to include new specs/ignore based on a boolean flag.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good. Will take it up in a future PR.

} else {
t, err = template.New("deployment-rbac-old.yaml").ParseFiles(path.Join("template", "deployment-rbac-old.yaml"))
}
if err != nil {
return errors.Wrap(err, "Failed to parse deployment-rbac.yaml")
}

deployFilePath := path.Join(templateOutputPath, namespace+"-deployment.yaml")
deployFile, err := os.Create(deployFilePath)
if err != nil {
return errors.Wrap(err, "Failed to create a deployment file from deployment-rbac.yaml")
return errors.Wrap(err, "Failed to create a deployment file")
}
defer deployFile.Close()

Expand Down
Loading