mirror of
https://gerrit.hackerspace.pl/hscloud
synced 2025-01-24 16:13:54 +00:00
radex
e36beba34c
Change-Id: Ic2b1d6a952dc194c0ee2fa1673ceb91c43799308 Reviewed-on: https://gerrit.hackerspace.pl/c/hscloud/+/1723 Reviewed-by: q3k <q3k@hackerspace.pl>
112 lines
2.9 KiB
Go
112 lines
2.9 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
|
|
"github.com/golang/glog"
|
|
"google.golang.org/protobuf/encoding/prototext"
|
|
admission "k8s.io/api/admission/v1beta1"
|
|
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
pb "code.hackerspace.pl/hscloud/cluster/admitomatic/config"
|
|
)
|
|
|
|
type service struct {
|
|
ingress ingressFilter
|
|
}
|
|
|
|
// newService creates an admitomatic service from a given prototext config.
|
|
func newService(configuration []byte) (*service, error) {
|
|
var cfg pb.Config
|
|
if err := prototext.Unmarshal(configuration, &cfg); err != nil {
|
|
return nil, fmt.Errorf("parsing config: %v", err)
|
|
}
|
|
|
|
s := service{}
|
|
|
|
for i, ad := range cfg.AllowDomain {
|
|
if ad.Namespace == "" {
|
|
ad.Namespace = "default"
|
|
}
|
|
if ad.Dns == "" {
|
|
return nil, fmt.Errorf("config entry %d: dns must be set", i)
|
|
}
|
|
if ad.Regexp {
|
|
if err := s.ingress.allowRegexp(ad.Namespace, ad.Dns); err != nil {
|
|
return nil, fmt.Errorf("config entry (regexp) %d: %v", i, err)
|
|
}
|
|
} else {
|
|
if err := s.ingress.allow(ad.Namespace, ad.Dns); err != nil {
|
|
return nil, fmt.Errorf("config entry %d: %v", i, err)
|
|
}
|
|
}
|
|
glog.Infof("Ingress: allowing %s in %s", ad.Dns, ad.Namespace)
|
|
}
|
|
s.ingress.anythingGoesNamespaces = cfg.AnythingGoesNamespace
|
|
return &s, nil
|
|
}
|
|
|
|
// handler is the main HTTP handler of the admitomatic service. It servers the
|
|
// AdmissionReview API, and is called by the Kubernetes API server to
|
|
// permit/deny creation/updating of resources.
|
|
func (s *service) handler(w http.ResponseWriter, r *http.Request) {
|
|
var body []byte
|
|
if r.Body != nil {
|
|
if data, err := ioutil.ReadAll(r.Body); err == nil {
|
|
body = data
|
|
}
|
|
}
|
|
|
|
if r.Method != "POST" {
|
|
glog.Errorf("%s %s: invalid method", r.Method, r.URL)
|
|
return
|
|
}
|
|
|
|
contentType := r.Header.Get("Content-Type")
|
|
if contentType != "application/json" {
|
|
glog.Errorf("%s %s: invalid content-type", r.Method, r.URL)
|
|
return
|
|
}
|
|
|
|
var review admission.AdmissionReview
|
|
if err := json.Unmarshal(body, &review); err != nil {
|
|
glog.Errorf("%s %s: cannot decode: %v", r.Method, r.URL, err)
|
|
return
|
|
}
|
|
|
|
if review.Kind != "AdmissionReview" {
|
|
glog.Errorf("%s %s: invalid Kind (%q)", r.Method, r.URL, review.Kind)
|
|
return
|
|
}
|
|
|
|
var err error
|
|
req := review.Request
|
|
resp := &admission.AdmissionResponse{
|
|
UID: req.UID,
|
|
Allowed: true,
|
|
}
|
|
switch {
|
|
case req.Kind.Group == "networking.k8s.io" && req.Kind.Kind == "Ingress":
|
|
resp, err = s.ingress.admit(req)
|
|
if err != nil {
|
|
glog.Errorf("%s %s %s: %v", req.Operation, req.Name, req.Namespace, err)
|
|
// Fail safe.
|
|
// TODO(q3k): monitor this?
|
|
resp = &admission.AdmissionResponse{
|
|
UID: req.UID,
|
|
Allowed: false,
|
|
Result: &meta.Status{
|
|
Code: 500,
|
|
Message: "admitomatic: internal server error",
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
glog.Infof("%s %s %s in %s: %v (%v)", req.Operation, req.Kind.Kind, req.Name, req.Namespace, resp.Allowed, resp.Result)
|
|
review.Response = resp
|
|
json.NewEncoder(w).Encode(review)
|
|
}
|