Skip to content

Commit

Permalink
chore: add new functions
Browse files Browse the repository at this point in the history
  • Loading branch information
moshloop committed Jul 13, 2023
1 parent 384222e commit c26c637
Show file tree
Hide file tree
Showing 10 changed files with 946 additions and 0 deletions.
16 changes: 16 additions & 0 deletions funcs/k8s.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package funcs

import (
"context"

"github.com/flanksource/gomplate/v3/k8s"
)

// CreateFilePathFuncs -
func CreateKubernetesFuncs(ctx context.Context) map[string]interface{} {
return map[string]interface{}{
"isHealthy": k8s.IsHealthy,
"getStatus": k8s.GetStatus,
"getHealth": k8s.GetHealth,
}
}
88 changes: 88 additions & 0 deletions k8s/k8s.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package k8s

import (
"fmt"

"github.com/flanksource/is-healthy/pkg/health"
"github.com/flanksource/is-healthy/pkg/lua"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"sigs.k8s.io/yaml"
)

type HealthStatus struct {
Status string `json:"status,omitempty"`
Message string `json:"message,omitempty"`
OK bool `json:"ok,omitempty"`
}

func GetUnstructured(in interface{}) *unstructured.Unstructured {
var err error
obj := make(map[string]interface{})

switch v := in.(type) {
case string:
err = yaml.Unmarshal([]byte(v), &obj)
case []byte:
err = yaml.Unmarshal(v, &obj)
case map[string]interface{}:
obj = v
case unstructured.Unstructured:
obj = v.Object
default:
var data []byte
if data, err = yaml.Marshal(in); err == nil {
err = yaml.Unmarshal(data, &obj)
}
}

if err != nil {
return nil
}

return &unstructured.Unstructured{Object: obj}
}

func IsHealthy(in interface{}) bool {
return GetHealth(in).OK
}

func GetStatus(in interface{}) string {
health := GetHealth(in)
return fmt.Sprintf("%s: %s", health.Status, health.Message)
}

func GetHealth(in interface{}) HealthStatus {
var err error
obj := GetUnstructured(in)

if obj == nil {
return HealthStatus{
OK: false,
Status: "Error",
Message: "Invalid spec",
}
}

_health, err := health.GetResourceHealth(obj, lua.ResourceHealthOverrides{})
if err != nil {
return HealthStatus{
OK: false,
Status: "Error",
Message: err.Error(),
}
}

if _health == nil {
return HealthStatus{
OK: false,
Status: "Missing",
Message: "No health check found",
}
}

return HealthStatus{
OK: _health.Status == health.HealthStatusHealthy || _health.Status == health.HealthStatusProgressing,
Status: string(_health.Status),
Message: _health.Message,
}
}
33 changes: 33 additions & 0 deletions k8s/k8s_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package k8s

import (
"testing"

"github.com/stretchr/testify/assert"
)

// test IsHealthy with a kubernetes pod spec in running mode
func TestIsHealthySvc(t *testing.T) {
r := GetHealth(TestHealthy)
assert.Equal(t, true, r.OK)
assert.Equal(t, "Healthy", r.Status)
}

func TestIsHealthyPod(t *testing.T) {
r := GetHealth(TestUnhealthy)
assert.Equal(t, false, r.OK)
assert.Equal(t, "Degraded", r.Status)
}

func TestIsHealthyAppset(t *testing.T) {
r := GetHealth(TestLuaStatus)
assert.Equal(t, false, r.OK)
assert.Equal(t, "Degraded", r.Status)
assert.Equal(t, "found less than two generators, Merge requires two or more", r.Message)
}

func TestIsHealthySvcPending(t *testing.T) {
r := GetHealth(TestProgressing)
assert.Equal(t, true, r.OK)
assert.Equal(t, "Progressing", r.Status)
}
175 changes: 175 additions & 0 deletions k8s/testdata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package k8s

var TestHealthy = `
apiVersion: v1
kind: Service
metadata:
name: argocd-server
spec:
clusterIP: 100.69.46.185
externalTrafficPolicy: Cluster
ports:
- name: http
nodePort: 30354
port: 80
protocol: TCP
targetPort: 8080
- name: https
nodePort: 31866
port: 443
protocol: TCP
targetPort: 8080
selector:
app: argocd-server
sessionAffinity: None
type: LoadBalancer
status:
loadBalancer:
ingress:
- hostname: abc123.us-west-2.elb.amazonaws.com
`

var TestProgressing = `
apiVersion: v1
kind: Service
metadata:
name: argo-artifacts
spec:
clusterIP: 10.105.70.181
externalTrafficPolicy: Cluster
ports:
- name: service
nodePort: 32667
port: 9000
protocol: TCP
targetPort: 9000
selector:
app: minio
release: argo-artifacts
sessionAffinity: None
type: LoadBalancer
status:
loadBalancer: {}
`

var TestUnhealthy = `
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: 2018-12-02T09:19:36Z
name: my-pod
namespace: argocd
resourceVersion: "151454"
selfLink: /api/v1/namespaces/argocd/pods/my-pod
uid: 63674389-f613-11e8-a057-fe5f49266390
spec:
containers:
- command:
- sh
- -c
- exit 1
image: alpine:latest
imagePullPolicy: Always
name: main
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-f9jvj
readOnly: true
dnsPolicy: ClusterFirst
nodeName: minikube
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: default
serviceAccountName: default
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
volumes:
- name: default-token-f9jvj
secret:
defaultMode: 420
secretName: default-token-f9jvj
status:
conditions:
- lastProbeTime: null
lastTransitionTime: 2018-12-02T09:19:36Z
status: "True"
type: Initialized
- lastProbeTime: null
lastTransitionTime: 2018-12-02T09:19:36Z
message: 'containers with unready status: [main]'
reason: ContainersNotReady
status: "False"
type: Ready
- lastProbeTime: null
lastTransitionTime: 2018-12-02T09:19:36Z
status: "True"
type: PodScheduled
containerStatuses:
- containerID: docker://c3aa0064b95a26045999b99c268e715a1c64201e816f1279ac06638778547bb8
image: alpine:latest
imageID: docker-pullable://alpine@sha256:621c2f39f8133acb8e64023a94dbdf0d5ca81896102b9e57c0dc184cadaf5528
lastState:
terminated:
containerID: docker://c3aa0064b95a26045999b99c268e715a1c64201e816f1279ac06638778547bb8
exitCode: 1
finishedAt: 2018-12-02T09:20:25Z
reason: Error
startedAt: 2018-12-02T09:20:25Z
name: main
ready: false
restartCount: 3
state:
waiting:
message: Back-off 40s restarting failed container=main pod=my-pod_argocd(63674389-f613-11e8-a057-fe5f49266390)
reason: CrashLoopBackOff
hostIP: 192.168.64.41
phase: Running
podIP: 172.17.0.9
qosClass: BestEffort
startTime: 2018-12-02T09:19:36Z
`

var TestLuaStatus = `
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: cluster-git
namespace: argocd
spec:
generators:
- merge:
generators: []
mergeKeys:
- server
template:
metadata:
name: '{{name}}'
spec:
destination:
namespace: default
server: '{{server}}'
project: default
source:
path: helm-guestbook
repoURL: https://github.com/argoproj/argocd-example-apps/
targetRevision: HEAD
status:
conditions:
- lastTransitionTime: "2021-11-12T14:28:01Z"
message: found less than two generators, Merge requires two or more
reason: ApplicationGenerationFromParamsError
status: "True"
type: ErrorOccurred
`
Loading

0 comments on commit c26c637

Please sign in to comment.