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

jg/json endpoint #11

Merged
merged 5 commits into from
Jan 26, 2019
Merged
Show file tree
Hide file tree
Changes from 4 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
160 changes: 25 additions & 135 deletions Gopkg.lock

Large diffs are not rendered by default.

21 changes: 20 additions & 1 deletion deploy/all.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,9 @@ spec:
spec:
serviceAccountName: fairwinds
containers:
- name: fairwinds
- name: webhook
image: quay.io/reactiveops/fairwinds
command: ["fairwinds", "--webhook"]
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it valid just to run a single container with both --webhook and --dashboard flags? Both ports could be specified in the container spec.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah i can do that, will just need to make a start up script or use a process manager, but that seems easy enough.

imagePullPolicy: Always
resources:
limits:
Expand All @@ -123,6 +124,24 @@ spec:
mountPath: /opt/app/config.yml
subPath: config.yml
readOnly: true
- name: dashboard
image: quay.io/reactiveops/fairwinds
command: ["fairwinds", "--dashboard"]
imagePullPolicy: Always
ports:
- containerPort: 8080
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
volumeMounts:
- name: fairwinds
mountPath: /opt/app/config.yml
subPath: config.yml
readOnly: true
volumes:
- name: fairwinds
configMap:
Expand Down
47 changes: 44 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,18 @@
package main

import (
"encoding/json"
"flag"
glog "log"
"net/http"
"os"

conf "github.com/reactiveops/fairwinds/pkg/config"
"github.com/reactiveops/fairwinds/pkg/kube"
"github.com/reactiveops/fairwinds/pkg/validator"
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
apitypes "k8s.io/apimachinery/pkg/types"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
"sigs.k8s.io/controller-runtime/pkg/client/config"
Expand All @@ -37,18 +42,54 @@ var FairwindsName = "fairwinds"
var log = logf.Log.WithName(FairwindsName)

func main() {
dashboard := flag.Bool("dashboard", false, "Runs the webserver for Fairwinds dashboard.")
webhook := flag.Bool("webhook", false, "Runs the webhook webserver.")

var disableWebhookConfigInstaller bool
flag.BoolVar(&disableWebhookConfigInstaller, "disable-webhook-config-installer", false,
"disable the installer in the webhook server, so it won't install webhook configuration resources during bootstrapping")

flag.Parse()
logf.SetLogger(logf.ZapLogger(false))
entryLog := log.WithName("entrypoint")

c, err := conf.ParseFile("config.yml")
if err != nil {
entryLog.Error(err, "parse config err")
return
}

if *webhook {
startWebhookServer(c, disableWebhookConfigInstaller)
}

if *dashboard {
startDashboardServer(c)
}
}

func startDashboardServer(c conf.Configuration) {
http.HandleFunc("/validate", func(w http.ResponseWriter, r *http.Request) { validateHandler(w, r, c) })
glog.Println("Starting Fairwinds dashboard webserver on port 8080.")
glog.Fatal(http.ListenAndServe(":8080", nil))
}

func validateHandler(w http.ResponseWriter, r *http.Request, c conf.Configuration) {
var results []validator.Results
pods, err := kube.CoreV1API.Pods("").List(metav1.ListOptions{})
Copy link
Contributor

Choose a reason for hiding this comment

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

At some point in the future it would be really nice to group/validate these results by deployment/daemon set/stateful set/etc to simplify the validation and output.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah def, i think the next step will be to validate deployments vs pods.

if err != nil {
return
}
glog.Println("pods count:", len(pods.Items))
for _, pod := range pods.Items {
result := validator.ValidatePods(c, &pod, validator.Results{})
results = append(results, result)
}
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(results)
}

func startWebhookServer(c conf.Configuration, disableWebhookConfigInstaller bool) {
logf.SetLogger(logf.ZapLogger(false))
entryLog := log.WithName("entrypoint")

// Setup a Manager
entryLog.Info("setting up manager")
Expand Down
61 changes: 61 additions & 0 deletions pkg/kube/clientset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package kube

import (
"fmt"
"os"
"path/filepath"

homedir "github.com/mitchellh/go-homedir"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)

func createClientset() *kubernetes.Clientset {
var err error
var config *rest.Config
kubeconfig := getKubeConfig()

switch kubeconfig {
case "":
config, err = rest.InClusterConfig()
default:
config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
}
if err != nil {
fmt.Println("Error:", err)
}

clientset, err := kubernetes.NewForConfig(config)
if err != nil {
fmt.Println("Error:", err)
}
return clientset
}

func getKubeConfig() string {
Copy link
Contributor

Choose a reason for hiding this comment

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

controller-runtime's config.getConfigOrDie() function should probably suffice here, and is already in use. Ideally that would also mean there was no need to add an additional package. https://github.com/reactiveops/fairwinds/blob/master/main.go#L55

var path string

if os.Getenv("KUBECONFIG") != "" {
path = os.Getenv("KUBECONFIG")
return path
}

if home, err := homedir.Dir(); err == nil {
path = filepath.Join(home, ".kube", "config")
}

if _, err := os.Stat(path); err != nil {
// No kubeconfig exists, therefor return an emtpy string to
// indicate that this web server is running inside the cluster.
return ""
}
return path
}

var clientset = createClientset()

// Kubernetes version 1.11 APIs
Copy link
Contributor

Choose a reason for hiding this comment

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

Why specify the specific API version here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah i don't really need to, let me remove that


// CoreV1API exports the v1 Core API client.
var CoreV1API = clientset.CoreV1()
5 changes: 3 additions & 2 deletions pkg/validator/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,14 @@ func (v *PodValidator) Handle(ctx context.Context, req types.Request) types.Resp
return admission.ErrorResponse(http.StatusBadRequest, err)
}

results := validatePods(v.Config, pod, Results{})
results := ValidatePods(v.Config, pod, Results{})
allowed, reason := results.Format()

return admission.ValidationResponse(allowed, reason)
}

func validatePods(conf conf.Configuration, pod *corev1.Pod, results Results) Results {
// ValidatePods does validates that each pod conforms to the Fairwinds config.
func ValidatePods(conf conf.Configuration, pod *corev1.Pod, results Results) Results {
for _, container := range pod.Spec.InitContainers {
results.InitContainerValidations = append(
results.InitContainerValidations,
Expand Down
21 changes: 21 additions & 0 deletions vendor/github.com/mitchellh/go-homedir/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions vendor/github.com/mitchellh/go-homedir/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions vendor/github.com/mitchellh/go-homedir/go.mod

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

157 changes: 157 additions & 0 deletions vendor/github.com/mitchellh/go-homedir/homedir.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.