Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update check_node_status to support other NodeCondition #309

Merged
merged 2 commits into from
Mar 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 89 additions & 35 deletions plugins/check_node_status/lib.go
Original file line number Diff line number Diff line change
@@ -1,78 +1,132 @@
package check_node_status

import (
"fmt"
"os"
"encoding/json"
"errors"

"github.com/appscode/go/flags"
"github.com/appscode/searchlight/pkg/icinga"
"github.com/appscode/searchlight/plugins"
"github.com/spf13/cobra"
core "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/tools/clientcmd"
)

type Request struct {
masterURL string
kubeconfigPath string
type plugin struct {
client corev1.NodeInterface
options options
}

var _ plugins.PluginInterface = &plugin{}

Name string
func newPlugin(client corev1.NodeInterface, opts options) *plugin {
return &plugin{client, opts}
}

func CheckNodeStatus(req *Request) (icinga.State, interface{}) {
config, err := clientcmd.BuildConfigFromFlags(req.masterURL, req.kubeconfigPath)
func newPluginFromConfig(opts options) (*plugin, error) {
config, err := clientcmd.BuildConfigFromFlags(opts.masterURL, opts.kubeconfigPath)
if err != nil {
return icinga.Unknown, err
return nil, err
}
kubeClient := kubernetes.NewForConfigOrDie(config)
client := kubernetes.NewForConfigOrDie(config).CoreV1().Nodes()
return newPlugin(client, opts), nil
}

type options struct {
masterURL string
kubeconfigPath string
// Icinga host name
hostname string
// options for Secret
nodeName string
}

node, err := kubeClient.CoreV1().Nodes().Get(req.Name, metav1.GetOptions{})
func (o *options) validate() error {
host, err := icinga.ParseHost(o.hostname)
if err != nil {
return icinga.Unknown, err
return errors.New("invalid icinga host.name")
}
if host.Type != icinga.TypeNode {
return errors.New("invalid icinga host type")
}
o.nodeName = host.ObjectName
return nil
}

if node == nil {
return icinga.Critical, "Node not found"
type message struct {
Ready core.ConditionStatus `json:"ready,omitempty"`
OutOfDisk core.ConditionStatus `json:"outOfDisk,omitempty"`
MemoryPressure core.ConditionStatus `json:"memoryPressure,omitempty"`
DiskPressure core.ConditionStatus `json:"diskPressure,omitempty"`
NetworkUnavailable core.ConditionStatus `json:"networkUnavailable,omitempty"`
}

func (p *plugin) Check() (icinga.State, interface{}) {
node, err := p.client.Get(p.options.nodeName, metav1.GetOptions{})
if err != nil {
return icinga.Unknown, err
}

msg := message{}
for _, condition := range node.Status.Conditions {
if condition.Type == core.NodeReady && condition.Status == core.ConditionFalse {
return icinga.Critical, "Node is not Ready"
switch condition.Type {
case core.NodeReady:
msg.Ready = condition.Status
case core.NodeOutOfDisk:
msg.OutOfDisk = condition.Status
case core.NodeMemoryPressure:
msg.MemoryPressure = condition.Status
case core.NodeDiskPressure:
msg.DiskPressure = condition.Status
case core.NodeNetworkUnavailable:
msg.NetworkUnavailable = condition.Status
}
}

return icinga.OK, "Node is Ready"
var state icinga.State
if msg.Ready == core.ConditionFalse {
state = icinga.Critical
} else if msg.OutOfDisk == core.ConditionTrue ||
msg.MemoryPressure == core.ConditionTrue ||
msg.DiskPressure == core.ConditionTrue ||
msg.NetworkUnavailable == core.ConditionTrue {
state = icinga.Critical
}

output, err := json.MarshalIndent(msg, "", " ")
if err != nil {
return icinga.Unknown, err
}

return state, string(output)
}

func NewCmd() *cobra.Command {
var req Request
var icingaHost string
var opts options

c := &cobra.Command{
Use: "check_node_status",
Short: "Check Kubernetes Node",
Example: "",
Use: "check_node_status",
Short: "Check Kubernetes Node",

Run: func(cmd *cobra.Command, args []string) {
flags.EnsureRequiredFlags(cmd, "host")

host, err := icinga.ParseHost(icingaHost)
if err != nil {
fmt.Fprintln(os.Stdout, icinga.Warning, "Invalid icinga host.name")
os.Exit(3)
if err := opts.validate(); err != nil {
icinga.Output(icinga.Unknown, err)
}
if host.Type != icinga.TypeNode {
fmt.Fprintln(os.Stdout, icinga.Warning, "Invalid icinga host type")
os.Exit(3)
plugin, err := newPluginFromConfig(opts)
if err != nil {
icinga.Output(icinga.Unknown, err)
}
req.Name = host.ObjectName
icinga.Output(CheckNodeStatus(&req))
icinga.Output(plugin.Check())
},
}

c.Flags().StringVar(&req.masterURL, "master", req.masterURL, "The address of the Kubernetes API server (overrides any value in kubeconfig)")
c.Flags().StringVar(&req.kubeconfigPath, "kubeconfig", req.kubeconfigPath, "Path to kubeconfig file with authorization information (the master location is set by the master flag).")

c.Flags().StringVarP(&icingaHost, "host", "H", "", "Icinga host name")
c.Flags().StringVar(&opts.masterURL, "master", opts.masterURL, "The address of the Kubernetes API server (overrides any value in kubeconfig)")
c.Flags().StringVar(&opts.kubeconfigPath, "kubeconfig", opts.kubeconfigPath, "Path to kubeconfig file with authorization information (the master location is set by the master flag).")
c.Flags().StringVarP(&opts.hostname, "host", "H", "", "Icinga host name")
return c
}
212 changes: 212 additions & 0 deletions plugins/check_node_status/lib_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
package check_node_status

import (
"github.com/appscode/searchlight/pkg/icinga"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
core "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
)

var _ = Describe("check_cert", func() {
var node *core.Node
var client corev1.NodeInterface
var opts options

BeforeEach(func() {
client = cs.CoreV1().Nodes()
node = &core.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node",
},
}
opts = options{
nodeName: node.Name,
}
})

AfterEach(func() {
if client != nil {
client.Delete(node.Name, &metav1.DeleteOptions{})
}
})

Describe("there is a ready node", func() {
Context("with no other problems", func() {
It("should be OK", func() {
_, err := client.Create(node)
Expect(err).ShouldNot(HaveOccurred())

node.Status.Conditions = []core.NodeCondition{
{
Type: core.NodeReady,
Status: core.ConditionTrue,
},
}
_, err = client.Update(node)
Expect(err).ShouldNot(HaveOccurred())

state, _ := newPlugin(client, opts).Check()
Expect(state).Should(BeIdenticalTo(icinga.OK))
})
})
Context("with other problems", func() {
It("such as OutOfDisk", func() {
_, err := client.Create(node)
Expect(err).ShouldNot(HaveOccurred())

node.Status.Conditions = []core.NodeCondition{
{
Type: core.NodeReady,
Status: core.ConditionTrue,
},
{
Type: core.NodeOutOfDisk,
Status: core.ConditionTrue,
},
}
_, err = client.Update(node)
Expect(err).ShouldNot(HaveOccurred())

state, _ := newPlugin(client, opts).Check()
Expect(state).Should(BeIdenticalTo(icinga.Critical))
})
It("such as OutOfDisk, MemoryPressure", func() {
_, err := client.Create(node)
Expect(err).ShouldNot(HaveOccurred())

node.Status.Conditions = []core.NodeCondition{
{
Type: core.NodeReady,
Status: core.ConditionTrue,
},
{
Type: core.NodeOutOfDisk,
Status: core.ConditionTrue,
},
{
Type: core.NodeMemoryPressure,
Status: core.ConditionTrue,
},
{
Type: core.NodeDiskPressure,
Status: core.ConditionTrue,
},
{
Type: core.NodeNetworkUnavailable,
Status: core.ConditionTrue,
},
}
_, err = client.Update(node)
Expect(err).ShouldNot(HaveOccurred())

state, _ := newPlugin(client, opts).Check()
Expect(state).Should(BeIdenticalTo(icinga.Critical))
})
})
})

Describe("there is a not ready node", func() {
JustBeforeEach(func() {
client = cs.CoreV1().Nodes()
opts = options{
nodeName: node.Name,
}
})
Context("with no other problems", func() {
It("should be Critical", func() {
_, err := client.Create(node)
Expect(err).ShouldNot(HaveOccurred())

node.Status.Conditions = []core.NodeCondition{
{
Type: core.NodeReady,
Status: core.ConditionFalse,
},
}
_, err = client.Update(node)
Expect(err).ShouldNot(HaveOccurred())

state, _ := newPlugin(client, opts).Check()
Expect(state).Should(BeIdenticalTo(icinga.Critical))
})
})
Context("with other problems", func() {
It("such as OutOfDisk", func() {
_, err := client.Create(node)
Expect(err).ShouldNot(HaveOccurred())

node.Status.Conditions = []core.NodeCondition{
{
Type: core.NodeReady,
Status: core.ConditionFalse,
},
{
Type: core.NodeOutOfDisk,
Status: core.ConditionTrue,
},
}
_, err = client.Update(node)
Expect(err).ShouldNot(HaveOccurred())

state, _ := newPlugin(client, opts).Check()
Expect(state).Should(BeIdenticalTo(icinga.Critical))
})
It("such as OutOfDisk, MemoryPressure", func() {
_, err := client.Create(node)
Expect(err).ShouldNot(HaveOccurred())

node.Status.Conditions = []core.NodeCondition{
{
Type: core.NodeReady,
Status: core.ConditionFalse,
},
{
Type: core.NodeOutOfDisk,
Status: core.ConditionTrue,
},
{
Type: core.NodeMemoryPressure,
Status: core.ConditionTrue,
},
}
_, err = client.Update(node)
Expect(err).ShouldNot(HaveOccurred())

state, _ := newPlugin(client, opts).Check()
Expect(state).Should(BeIdenticalTo(icinga.Critical))
})
})
})

Describe("Check validation", func() {
Context("for invalid", func() {
It("with invalid part", func() {
opts = options{
hostname: "demo@node",
}
err := opts.validate()
Expect(err).Should(HaveOccurred())
})
It("with invalid type", func() {
opts = options{
hostname: "demo@cluster",
}
err := opts.validate()
Expect(err).Should(HaveOccurred())
})
})
Context("for valid", func() {
It("with valid name", func() {
opts = options{
hostname: "demo@node@node",
}
err := opts.validate()
Expect(err).ShouldNot(HaveOccurred())

Expect(opts.nodeName).Should(Equal("node"))
})
})
})
})
Loading