Exemple #1
0
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
}
Exemple #2
0
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
}
Exemple #3
0
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
}
Exemple #4
0
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
}
Exemple #5
0
// 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
}
Exemple #6
0
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)
	}
}
Exemple #7
0
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)
		}
	}
}
Exemple #8
0
// 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
}