blob: 149a11dbd5ebb7a8288c1c7b01d87e455befe135 [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.")
giolekvab22550e2020-05-12 22:09:03 +040026var helmBin = flag.String("helm_bin", "/usr/local/bin/helm", "Path to the Helm binary.")
giolekvaa4a153b2020-05-12 11:49:53 +040027var port = flag.Int("port", 1234, "Port to listen on.")
giolekvafe0765f2020-05-12 14:09:09 +040028var apiAddr = flag.String("api_addr", "", "PCloud API service address.")
giolekvaa4a153b2020-05-12 11:49:53 +040029
30var helmUploadPage = `
31<html>
32<head>
33 <title>Upload Helm chart</title>
34</head>
35<body>
giolekvab22550e2020-05-12 22:09:03 +040036<form enctype="multipart/form-data" method="post">
giolekvaa4a153b2020-05-12 11:49:53 +040037 <input type="file" name="chartfile" />
38 <input type="submit" value="upload" />
39</form>
40</body>
41</html>
42`
43
giolekvaebea5822020-05-12 20:52:19 +040044type handler struct {
45 nsClient corev1.NamespaceInterface
46}
47
48func (hn *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
giolekvaa4a153b2020-05-12 11:49:53 +040049 if r.Method == "GET" {
50 _, err := io.WriteString(w, helmUploadPage)
51 if err != nil {
52 http.Error(w, err.Error(), http.StatusInternalServerError)
53 }
54 } else if r.Method == "POST" {
55 r.ParseMultipartForm(1000000)
56 file, handler, err := r.FormFile("chartfile")
57 if err != nil {
58 http.Error(w, err.Error(), http.StatusInternalServerError)
59 return
60 }
61 defer file.Close()
giolekvaebea5822020-05-12 20:52:19 +040062 tmp := uuid.New().String()
63 if tmp == "" {
64 http.Error(w, "Could not generate temp dir", http.StatusInternalServerError)
65 return
66 }
67 p := "/tmp/" + tmp
68 // TODO(giolekva): defer rmdir
69 if err := syscall.Mkdir(p, 0777); err != nil {
70 http.Error(w, "Could not create temp dir", http.StatusInternalServerError)
71 return
72 }
73 p += "/" + handler.Filename
giolekvaa4a153b2020-05-12 11:49:53 +040074 f, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE, 0666)
75 if err != nil {
76 fmt.Println(err)
77 return
78 }
79 defer f.Close()
80 _, err = io.Copy(f, file)
81 if err != nil {
82 http.Error(w, err.Error(), http.StatusInternalServerError)
83 return
84 }
giolekvaebea5822020-05-12 20:52:19 +040085 if err = installHelmChart(p, hn.nsClient); err != nil {
giolekvaa4a153b2020-05-12 11:49:53 +040086 http.Error(w, err.Error(), http.StatusInternalServerError)
87 return
88 }
89 w.Write([]byte("Installed"))
giolekvad5a58c42020-05-07 22:18:35 +040090 }
giolekvaa4a153b2020-05-12 11:49:53 +040091}
92
giolekvaebea5822020-05-12 20:52:19 +040093func installHelmChart(path string, nsClient corev1.NamespaceInterface) error {
94 h, err := app.HelmChartFromTar(path)
giolekvaa4a153b2020-05-12 11:49:53 +040095 if err != nil {
96 return err
97 }
giolekvaebea5822020-05-12 20:52:19 +040098 if err = app.InstallSchema(h.Schema, *apiAddr); err != nil {
giolekvafe0765f2020-05-12 14:09:09 +040099 return err
100 }
101 glog.Infof("Installed schema: %s", h.Schema)
giolekvaebea5822020-05-12 20:52:19 +0400102 namespace := fmt.Sprintf("app-%s", h.Name)
103 if err = createNamespace(nsClient, namespace); err != nil {
104 return err
105 }
106 glog.Infof("Created namespaces: %s", namespace)
107 if err = h.Install(
giolekvab22550e2020-05-12 22:09:03 +0400108 *helmBin,
giolekvaebea5822020-05-12 20:52:19 +0400109 map[string]string{}); err != nil {
110 return err
111 }
112 glog.Info("Installed")
113 return nil
114}
115
116func createNamespace(nsClient corev1.NamespaceInterface, name string) error {
117 _, err := nsClient.Create(
118 context.TODO(),
119 &apiv1.Namespace{
120 ObjectMeta: metav1.ObjectMeta{
121 Name: name}},
122 metav1.CreateOptions{})
giolekvaa4a153b2020-05-12 11:49:53 +0400123 return err
124}
125
giolekvaebea5822020-05-12 20:52:19 +0400126func getKubeConfig() (*rest.Config, error) {
127 if *kubeconfig != "" {
128 return clientcmd.BuildConfigFromFlags("", *kubeconfig)
129 } else {
130 return rest.InClusterConfig()
131 }
132}
133
giolekvaa4a153b2020-05-12 11:49:53 +0400134func main() {
135 flag.Parse()
giolekvaebea5822020-05-12 20:52:19 +0400136 config, err := getKubeConfig()
137 if err != nil {
138 glog.Fatalf("Could not initialize Kubeconfig: %v", err)
139 }
140 clientset, err := kubernetes.NewForConfig(config)
141 if err != nil {
142 glog.Fatalf("Could not create Kubernetes API client: %v", err)
143 }
144 namespaces := clientset.CoreV1().Namespaces()
145 http.Handle("/", &handler{namespaces})
giolekvaa4a153b2020-05-12 11:49:53 +0400146 log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
147
giolekvad5a58c42020-05-07 22:18:35 +0400148}