blob: b5f76625a16680df46260cf5ecddb1bb4abacc5a [file] [log] [blame]
Serge Bazanskic6118642021-01-31 01:17:38 +01001package main
2
3import (
4 "encoding/json"
5 "fmt"
6 "io/ioutil"
7 "net/http"
8
9 "github.com/golang/glog"
10 "google.golang.org/protobuf/encoding/prototext"
11 admission "k8s.io/api/admission/v1beta1"
12 meta "k8s.io/apimachinery/pkg/apis/meta/v1"
13
14 pb "code.hackerspace.pl/hscloud/cluster/admitomatic/config"
15)
16
17type service struct {
18 ingress ingressFilter
19}
20
21// newService creates an admitomatic service from a given prototext config.
22func newService(configuration []byte) (*service, error) {
23 var cfg pb.Config
24 if err := prototext.Unmarshal(configuration, &cfg); err != nil {
25 return nil, fmt.Errorf("parsing config: %v", err)
26 }
27
28 s := service{}
29
30 for i, ad := range cfg.AllowDomain {
31 if ad.Namespace == "" {
32 ad.Namespace = "default"
33 }
34 if ad.Dns == "" {
35 return nil, fmt.Errorf("config entry %d: dns must be set", i)
36 }
37 if err := s.ingress.allow(ad.Namespace, ad.Dns); err != nil {
38 return nil, fmt.Errorf("config entry %d: %v", i, err)
39 }
40 glog.Infof("Ingress: allowing %s in %s", ad.Dns, ad.Namespace)
41 }
Serge Bazanskic1f37252023-06-19 21:56:29 +000042 s.ingress.anythingGoesNamespaces = cfg.AnythingGoesNamespace
Serge Bazanskic6118642021-01-31 01:17:38 +010043 return &s, nil
44}
45
46// handler is the main HTTP handler of the admitomatic service. It servers the
47// AdmissionReview API, and is called by the Kubernetes API server to
48// permit/deny creation/updating of resources.
49func (s *service) handler(w http.ResponseWriter, r *http.Request) {
50 var body []byte
51 if r.Body != nil {
52 if data, err := ioutil.ReadAll(r.Body); err == nil {
53 body = data
54 }
55 }
56
57 if r.Method != "POST" {
58 glog.Errorf("%s %s: invalid method", r.Method, r.URL)
59 return
60 }
61
62 contentType := r.Header.Get("Content-Type")
63 if contentType != "application/json" {
64 glog.Errorf("%s %s: invalid content-type", r.Method, r.URL)
65 return
66 }
67
68 var review admission.AdmissionReview
69 if err := json.Unmarshal(body, &review); err != nil {
70 glog.Errorf("%s %s: cannot decode: %v", r.Method, r.URL, err)
71 return
72 }
73
74 if review.Kind != "AdmissionReview" {
75 glog.Errorf("%s %s: invalid Kind (%q)", r.Method, r.URL, review.Kind)
76 return
77 }
78
79 var err error
80 req := review.Request
81 resp := &admission.AdmissionResponse{
82 UID: req.UID,
83 Allowed: true,
84 }
85 switch {
86 case req.Kind.Group == "networking.k8s.io" && req.Kind.Kind == "Ingress":
87 resp, err = s.ingress.admit(req)
88 if err != nil {
89 glog.Errorf("%s %s %s: %v", req.Operation, req.Name, req.Namespace, err)
90 // Fail safe.
91 // TODO(q3k): monitor this?
92 resp = &admission.AdmissionResponse{
93 UID: req.UID,
94 Allowed: false,
95 Result: &meta.Status{
96 Code: 500,
97 Message: "admitomatic: internal server error",
98 },
99 }
100 }
101 }
102
103 glog.Infof("%s %s %s in %s: %v (%v)", req.Operation, req.Kind.Kind, req.Name, req.Namespace, resp.Allowed, resp.Result)
104 review.Response = resp
105 json.NewEncoder(w).Encode(review)
106}