blob: dd42825f45c4c4c7655bdd1749161da5a6fc5a87 [file] [log] [blame]
giolekvad5a58c42020-05-07 22:18:35 +04001package main
2
3import (
giolekvaebea5822020-05-12 20:52:19 +04004 "context"
giolekvaa4a153b2020-05-12 11:49:53 +04005 "flag"
6 "fmt"
7 "io"
8 "log"
9 "net/http"
10 "os"
giolekvaebea5822020-05-12 20:52:19 +040011 "syscall"
giolekvad5a58c42020-05-07 22:18:35 +040012
giolekvafe0765f2020-05-12 14:09:09 +040013 "github.com/golang/glog"
giolekvaebea5822020-05-12 20:52:19 +040014 "github.com/google/uuid"
15 apiv1 "k8s.io/api/core/v1"
16 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17 "k8s.io/client-go/kubernetes"
18 corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
19 "k8s.io/client-go/rest"
20 "k8s.io/client-go/tools/clientcmd"
giolekvaa4a153b2020-05-12 11:49:53 +040021
22 app "github.com/giolekva/pcloud/appmanager"
giolekvad5a58c42020-05-07 22:18:35 +040023)
24
giolekvaebea5822020-05-12 20:52:19 +040025var kubeconfig = flag.String("kubeconfig", "", "Absolute path to the kubeconfig file.")
giolekvaa4a153b2020-05-12 11:49:53 +040026var port = flag.Int("port", 1234, "Port to listen on.")
giolekvafe0765f2020-05-12 14:09:09 +040027var apiAddr = flag.String("api_addr", "", "PCloud API service address.")
giolekvaa4a153b2020-05-12 11:49:53 +040028
29var helmUploadPage = `
30<html>
31<head>
32 <title>Upload Helm chart</title>
33</head>
34<body>
35<form enctype="multipart/form-data" action="/" method="post">
36 <input type="file" name="chartfile" />
37 <input type="submit" value="upload" />
38</form>
39</body>
40</html>
41`
42
giolekvaebea5822020-05-12 20:52:19 +040043type handler struct {
44 nsClient corev1.NamespaceInterface
45}
46
47func (hn *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
giolekvaa4a153b2020-05-12 11:49:53 +040048 if r.Method == "GET" {
49 _, err := io.WriteString(w, helmUploadPage)
50 if err != nil {
51 http.Error(w, err.Error(), http.StatusInternalServerError)
52 }
53 } else if r.Method == "POST" {
54 r.ParseMultipartForm(1000000)
55 file, handler, err := r.FormFile("chartfile")
56 if err != nil {
57 http.Error(w, err.Error(), http.StatusInternalServerError)
58 return
59 }
60 defer file.Close()
giolekvaebea5822020-05-12 20:52:19 +040061 tmp := uuid.New().String()
62 if tmp == "" {
63 http.Error(w, "Could not generate temp dir", http.StatusInternalServerError)
64 return
65 }
66 p := "/tmp/" + tmp
67 // TODO(giolekva): defer rmdir
68 if err := syscall.Mkdir(p, 0777); err != nil {
69 http.Error(w, "Could not create temp dir", http.StatusInternalServerError)
70 return
71 }
72 p += "/" + handler.Filename
giolekvaa4a153b2020-05-12 11:49:53 +040073 f, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE, 0666)
74 if err != nil {
75 fmt.Println(err)
76 return
77 }
78 defer f.Close()
79 _, err = io.Copy(f, file)
80 if err != nil {
81 http.Error(w, err.Error(), http.StatusInternalServerError)
82 return
83 }
giolekvaebea5822020-05-12 20:52:19 +040084 if err = installHelmChart(p, hn.nsClient); err != nil {
giolekvaa4a153b2020-05-12 11:49:53 +040085 http.Error(w, err.Error(), http.StatusInternalServerError)
86 return
87 }
88 w.Write([]byte("Installed"))
giolekvad5a58c42020-05-07 22:18:35 +040089 }
giolekvaa4a153b2020-05-12 11:49:53 +040090}
91
giolekvaebea5822020-05-12 20:52:19 +040092func installHelmChart(path string, nsClient corev1.NamespaceInterface) error {
93 h, err := app.HelmChartFromTar(path)
giolekvaa4a153b2020-05-12 11:49:53 +040094 if err != nil {
95 return err
96 }
giolekvaebea5822020-05-12 20:52:19 +040097 if err = app.InstallSchema(h.Schema, *apiAddr); err != nil {
giolekvafe0765f2020-05-12 14:09:09 +040098 return err
99 }
100 glog.Infof("Installed schema: %s", h.Schema)
giolekvaebea5822020-05-12 20:52:19 +0400101 namespace := fmt.Sprintf("app-%s", h.Name)
102 if err = createNamespace(nsClient, namespace); err != nil {
103 return err
104 }
105 glog.Infof("Created namespaces: %s", namespace)
106 if err = h.Install(
giolekvaa4a153b2020-05-12 11:49:53 +0400107 "/usr/local/bin/helm",
giolekvaebea5822020-05-12 20:52:19 +0400108 map[string]string{}); err != nil {
109 return err
110 }
111 glog.Info("Installed")
112 return nil
113}
114
115func createNamespace(nsClient corev1.NamespaceInterface, name string) error {
116 _, err := nsClient.Create(
117 context.TODO(),
118 &apiv1.Namespace{
119 ObjectMeta: metav1.ObjectMeta{
120 Name: name}},
121 metav1.CreateOptions{})
giolekvaa4a153b2020-05-12 11:49:53 +0400122 return err
123}
124
giolekvaebea5822020-05-12 20:52:19 +0400125func getKubeConfig() (*rest.Config, error) {
126 if *kubeconfig != "" {
127 return clientcmd.BuildConfigFromFlags("", *kubeconfig)
128 } else {
129 return rest.InClusterConfig()
130 }
131}
132
giolekvaa4a153b2020-05-12 11:49:53 +0400133func main() {
134 flag.Parse()
giolekvaebea5822020-05-12 20:52:19 +0400135 config, err := getKubeConfig()
136 if err != nil {
137 glog.Fatalf("Could not initialize Kubeconfig: %v", err)
138 }
139 clientset, err := kubernetes.NewForConfig(config)
140 if err != nil {
141 glog.Fatalf("Could not create Kubernetes API client: %v", err)
142 }
143 namespaces := clientset.CoreV1().Namespaces()
144 http.Handle("/", &handler{namespaces})
giolekvaa4a153b2020-05-12 11:49:53 +0400145 log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
146
giolekvad5a58c42020-05-07 22:18:35 +0400147}