func (s *Server) register(r *kite.Request) (interface{}, error) { var req RegisterRequest if r.Args != nil { err := r.Args.One().Unmarshal(&req) if err != nil { return nil, errors.New("invalid request: " + err.Error()) } } // register requests issued by managed kites always have Username // empty if req.Username == "" { req.Username = r.Username } // try to always have distinct name for the tunnel, a single user // can have more than one tunnel; for tunnels created by kloud // the TunnelName is always distinct (= jMachine.Uid), for // managed kites we need to generate the name here if req.TunnelName == "" { req.TunnelName = utils.RandString(12) } vhost := s.vhost(&req) s.opts.Log.Debug("received register request: %s", TunnelsByName(toList(req.Services, vhost))) // By default all addresses are routed by default with wildcard // CNAME. If it is disabled explicitly, we're inserting A records // for each tunnel instead. if s.opts.NoCNAME { if err := s.upsert(vhost); err != nil { return nil, err } } res := &RegisterResult{ VirtualHost: vhost, ServerAddr: s.opts.ServerAddr, Ident: utils.RandString(32), } s.addClient(res.Ident, req.TunnelName, res.VirtualHost, req.Services) s.Server.OnDisconnect(res.Ident, func() error { s.delClient(res.Ident, res.VirtualHost) return nil }) return res, nil }
func (cmd *TeamInit) Valid() error { if cmd.KlientID == "" { return errors.New("empty value for -klient flag") } if cmd.Provider == "" { cmd.Provider = "vagrant" } if cmd.Provider != "vagrant" { return errors.New("currently only vagrant is supported") } if cmd.Team == "" { cmd.Team = utils.RandString(12) } if cmd.StackTemplate == "-" { p, err := ioutil.ReadAll(os.Stdin) if err != nil { return err } cmd.StackTemplate = string(p) } else { p, err := ioutil.ReadFile(cmd.StackTemplate) if err != nil { return err } cmd.StackTemplate = string(p) } if cmd.StackTemplate == "" { return errors.New("empty value for -stack flag") } return nil }
func (s *Stack) injectMetadata(app map[string]interface{}, name string) error { envs := getObject(app["env"]) s.Builder.InterpolateField(app, name, "cmd") cmd, ok := app["cmd"] if ok { delete(app, "cmd") envs["KODING_CMD"] = cmd } if val, ok := envs["KODING_KLIENT_URL"].(string); !ok || val == "" { envs["KODING_KLIENT_URL"] = s.KlientURL } for i, label := range s.Labels { kiteKey, err := s.BuildKiteKey(label.Label, s.Req.Username) if err != nil { return err } tunnelID := utils.RandString(8) if m, ok := s.Builder.Machines[label.Label]; ok && m.Uid != "" { tunnelID = m.Uid } konfig := &config.Konfig{ Endpoints: stack.Konfig.Endpoints, TunnelID: tunnelID, KiteKey: kiteKey, Debug: s.Debug, } metadata := map[string]interface{}{ "konfig.konfig.konfigs": map[string]interface{}{ konfig.ID(): konfig, }, "konfig.konfig.konfigs.used": map[string]interface{}{ "id": konfig.ID(), }, } p, err := json.Marshal(metadata) if err != nil { return err } envs[fmt.Sprintf("KODING_METADATA_%d", i+1)] = base64.StdEncoding.EncodeToString(p) } app["env"] = envs return nil }
func (s *Stack) newTunnel(resourceName string) *Tunnel { t := &Tunnel{ Name: utils.RandString(12), KiteURL: s.TunnelURL.String(), } if m, ok := s.Builder.Machines[resourceName]; ok { t.Name = m.Uid } return t }
// Auth provides the api.AuthFunc function for use with clients' transports. // // It never fails and always returns a valid session. func (fa *FakeAuth) Auth(opts *api.AuthOptions) (*api.Session, error) { fa.mu.Lock() defer fa.mu.Unlock() session, ok := fa.Sessions[opts.User.String()] if !ok || opts.Refresh { session = &api.Session{ ClientID: utils.RandString(12), User: &api.User{ Username: opts.User.Username, Team: opts.User.Team, }, } fa.Sessions[opts.User.String()] = session } return session, nil }
func (n *Ngrok) Start(subdomain, localAddr string) (string, error) { if n.process != nil { return "", errors.New("ngrok tunnel is already running") } if subdomain == "" { subdomain = utils.RandString(12) } p := n.cmd("-log", "stdout", "-authtoken", Test.NgrokToken, "-subdomain", subdomain, localAddr) if err := p.Start(); err != nil { return "", err } // wait for tunnel to get up tunnelHost := subdomain + ".ngrok.com" tunnelURL := "http://" + tunnelHost ctx, cancel := context.WithTimeout(context.Background(), n.StartTimeout) defer cancel() for { resp, err := ctxhttp.Get(ctx, nil, tunnelURL) if err == context.DeadlineExceeded { return "", fmt.Errorf("timed out after %s waiting for %s to be ready", n.StartTimeout, tunnelURL) } if err == nil { resp.Body.Close() // 404 means tunnel not found if resp.StatusCode != http.StatusNotFound { n.process = p return tunnelHost, nil } } time.Sleep(1 * time.Second) } }
func testTunnelserverHTTP(t *testing.T, serverURL *url.URL) { // Create and start local server. All requests to this server are tunneled. received := make(chan *http.Request, 32) localServer := Test.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { received <- r w.WriteHeader(204) })) // Create and start Tunnel Client. It takes care of forwarding requests // from the internet to the loceal server. var virtualHost string var wg sync.WaitGroup wg.Add(1) clientCfg, _ := Test.GenKiteConfig() k := kite.New("tunnelclient", "0.0.1") k.Config = clientCfg clientOpts := &tunnelproxy.ClientOptions{ LastVirtualHost: serverURL.Host, LocalAddr: host(localServer.URL), Kite: k, OnRegister: func(req *tunnelproxy.RegisterResult) { virtualHost = req.VirtualHost wg.Done() }, Log: Test.Log.New("tunnelclient"), Debug: true, Timeout: 1 * time.Minute, } client, err := tunnelproxy.NewClient(clientOpts) if err != nil { t.Fatalf("error creating tunnelproxy client: %s", err) } client.Start() defer client.Close() wg.Wait() if virtualHost == "" { t.Fatal("expected virtualHost to be non-empty") } cases := []struct { Method string URL *url.URL }{{ // i=0 Method: "GET", URL: &url.URL{ Scheme: "http", Host: serverURL.Host, Path: "/", }, }, { // i=1 Method: "GET", URL: &url.URL{ Scheme: "http", Host: serverURL.Host, Path: "/foo", }, }, { // i=2 Method: "DELETE", URL: &url.URL{ Scheme: "http", Host: serverURL.Host, Path: "/foo/bar", }, }, { // i=3 Method: "POST", URL: &url.URL{ Scheme: "http", Host: serverURL.Host, Path: "/" + utils.RandString(32), }, }, { // i=4 Method: "PUT", URL: &url.URL{ Scheme: "http", Host: serverURL.Host, Path: "/" + utils.RandString(32), }, }} // Send requests to public end of the tunnel. for i, cas := range cases { req, err := http.NewRequest(cas.Method, cas.URL.String(), nil) if err != nil { t.Fatalf("%d: error creating request to %s: %s", i, cas.URL, err) } req.Host = virtualHost _, err = http.DefaultClient.Do(req) if err != nil { t.Fatalf("%d: error sending request to %s: %s", i, cas.URL, err) } } timeout := time.After(1 * time.Minute) // Ensure all the requests were forwarded. for i, cas := range cases { select { case req := <-received: if req.Method != cas.Method { t.Errorf("%d: want Method=%q; got %q", i, cas.Method, req.Method) } if req.URL.Path != cas.URL.Path { t.Errorf("%d: want Path=%q; got %q", i, cas.URL.Path, req.URL.Path) } case <-timeout: t.Fatalf("receiving requests from %s timed out", localServer.URL) } } }
// InjectVagrantData sets default properties for vagrant_instance Terraform template. func (s *Stack) ApplyTemplate(c *stack.Credential) (*stack.Template, error) { t := s.Builder.Template cred := c.Credential.(*Cred) var res VagrantResource if err := t.DecodeResource(&res); err != nil { return nil, err } if len(res.Build) == 0 { return nil, errors.New("no vagrant instances specified") } uids := s.Builder.MachineUIDs() s.Log.Debug("machine uids (%d): %v", len(uids), uids) klientURL, err := s.Session.Userdata.LookupKlientURL() if err != nil { return nil, err } // queryString is taken from jCredentialData.meta.queryString, // for debugging purposes it can be overwritten in the template, // however if template has multiple machines, all of them // are required to overwrite the queryString to the same value // to match current implementation of terraformplugins/vagrant // provider. // // Otherwise we fail early to show problem with the template. var queryString string for resourceName, box := range res.Build { kiteKey, err := s.BuildKiteKey(resourceName, s.Req.Username) if err != nil { return nil, err } // set kontrolURL if not provided via template kontrolURL := s.Session.Userdata.Keycreator.KontrolURL if k, ok := box["kontrolURL"].(string); ok { kontrolURL = k } else { box["kontrolURL"] = kontrolURL } if q, ok := box["queryString"].(string); ok { q, err := utils.QueryString(q) if err != nil { return nil, fmt.Errorf("%s: error reading queryString: %s", resourceName, err) } if queryString != "" && queryString != q { return nil, fmt.Errorf("mismatched queryString provided for multiple instances; want %q, got %q", queryString, q) } queryString = q } else { box["queryString"] = "${var.vagrant_queryString}" queryString = cred.QueryString } // set default filePath to relative <stackdir>/<boxname>; for // default configured klient it resolves to ~/.vagrant.d/<stackdir>/<boxname> if _, ok := box["filePath"]; !ok { uid, ok := uids[resourceName] if !ok { // For Plan call we return random uid as it won't be returned // as a part of meta; the filePath is inserted into meta by // the apply method. uid = resourceName + "-" + utils.RandString(6) } box["filePath"] = "koding/${var.koding_group_slug}/" + uid } // set default CPU number if _, ok := box["cpus"]; !ok { box["cpus"] = "${var.vagrant_cpus}" } // set default RAM in MiB if _, ok := box["memory"]; !ok { box["memory"] = "${var.vagrant_memory}" } // set default box type if _, ok := box["box"]; !ok { box["box"] = "${var.vagrant_box}" } var ports []interface{} switch p := box["forwarded_ports"].(type) { case []interface{}: ports = p case []map[string]interface{}: ports = make([]interface{}, len(p)) for i := range p { ports[i] = p[i] } } // klient kite port kitePort := &vagrantapi.ForwardedPort{ HostPort: 2200, GuestPort: 56789, } // tlsproxy port kitesPort := &vagrantapi.ForwardedPort{ HostPort: 2201, GuestPort: 56790, } ports = append(ports, kitePort, kitesPort) box["forwarded_ports"] = ports box["username"] = s.Req.Username tunnel := s.newTunnel(resourceName) s.Builder.InterpolateField(box, resourceName, "user_data") if b, ok := box["debug"].(bool); ok && b { s.Debug = true } data := puser.Value{ Username: s.Req.Username, Groups: []string{"sudo"}, Hostname: s.Req.Username, // no typo here. hostname = username KiteKey: kiteKey, LatestKlientURL: klientURL, TunnelName: tunnel.Name, TunnelKiteURL: tunnel.KiteURL, KontrolURL: kontrolURL, } // pass the values as a JSON encoded as base64. Our script will decode // and unmarshall and use it inside the Vagrant box val, err := json.Marshal(&data) if err != nil { return nil, err } // Compressing the provision data isn't doing any serious optimizations, // it's just here so the debug output does not take half a screen. // // The provisionclient handles both compressed and uncompressed JSONs. var buf bytes.Buffer if cw, err := gzip.NewWriterLevel(&buf, 9); err == nil { if _, err = io.Copy(cw, bytes.NewReader(val)); err == nil && cw.Close() == nil { s.Log.Debug("using compressed provision data: %d vs %d", len(val), len(buf.Bytes())) val = buf.Bytes() } } box["provisionData"] = base64.StdEncoding.EncodeToString(val) res.Build[resourceName] = box } t.Resource["vagrant_instance"] = res.Build if err := t.Flush(); err != nil { return nil, err } content, err := t.JsonOutput() if err != nil { return nil, err } return &stack.Template{ Content: content, }, nil }