func (k *Klient) send(queryString, method string, req, resp interface{}) (string, error) { queryString, err := utils.QueryString(queryString) if err != nil { return "", err } k.Log.Debug("calling %q method on %q with %+v", method, queryString, req) kref, err := klient.ConnectTimeout(k.Kite, queryString, k.dialTimeout()) if err != nil { k.Log.Debug("connecting to %q klient failed: %s", queryString, err) return "", err } defer kref.Close() r, err := kref.Client.TellWithTimeout(method, k.timeout(), req) if err != nil { k.Log.Debug("sending request to %q klient failed: %s", queryString, err) return "", err } if err := r.Unmarshal(resp); err != nil { return "", errors.New("reading response from klient failed: " + err.Error()) } k.Log.Debug("received %+v response from %q (%q)", resp, method, queryString) return kref.URL(), nil }
func (p *Planner) checkSingleKlient(k *kite.Kite, label, kiteID string) *DialState { kiteID, err := utils.QueryString(kiteID) if err != nil { return &DialState{ Label: label, KiteID: kiteID, State: "dial", Err: err, } } start := time.Now() c, err := klient.NewWithTimeout(k, kiteID, p.klientTimeout()) if err == klient.ErrDialingFailed { return &DialState{ Label: label, KiteID: kiteID, KiteURL: c.Client.URL, State: "dial", Err: err, } } if err != nil { return &DialState{ Label: label, KiteID: kiteID, State: "kontrol", Err: err, } } defer c.Close() left := p.klientTimeout() - time.Now().Sub(start) err = c.PingTimeout(max(left, klient.DefaultTimeout)) if err != nil { return &DialState{ Label: label, KiteID: kiteID, State: "ping", Err: err, } } if p.OnDial != nil { err = p.OnDial(c.Client) } return &DialState{ Label: label, KiteID: kiteID, KiteURL: c.Client.URL, State: "provider", Err: err, } }
// Valid implements the kloud.Validator interface. func (meta *Cred) Valid() error { if meta.QueryString == "" { return errors.New("vagrant meta: query string is empty") } query, err := utils.QueryString(meta.QueryString) if err != nil { return err } meta.QueryString = query return nil }
// GetGroupForKite reverse looks up a team name for the given kiteID // by looking up a kiteID among jMachine.queryString fields. // // TODO(rjeczalik): This method does client-side filtering, // due to lack of indexes on the following fields: // // - registerUrl // - queryString // - ipAddress // // After the indexes are in place, the LookupGroup should // be reworked to look up on jMachine.queryString only. func LookupGroup(opts *LookupGroupOptions) (*models.Group, error) { var m []struct { RegisterURL string `bson:"registerUrl"` QueryString string `bson:"queryString"` IPAddress string `bson:"ipAddress"` Groups []struct { ID bson.ObjectId `bson:"id"` } `bson:"groups"` } id, err := GetUserID(opts.Username) if err != nil { return nil, err } fn := func(c *mgo.Collection) error { return c.Find(bson.M{"users.id": id}).Select(lookupGroupFields).All(&m) } if err := Mongo.Run(MachinesColl, fn); err != nil && err != mgo.ErrNotFound { return nil, err } // Look for questString. if qs, err := utils.QueryString(opts.KiteID); err == nil && qs != "" { for i := range m { if len(m[i].Groups) == 0 { continue } if m[i].QueryString == qs { return GetGroupById(m[i].Groups[0].ID.Hex()) } } } if host, err := parseHost(opts.ClientURL); err == nil && host != "" { // Look up for ipAddress. for i := range m { if len(m[i].Groups) == 0 { continue } mHost := m[i].IPAddress if host, _, err := net.SplitHostPort(mHost); err == nil { mHost = host } if mHost == host { return GetGroupById(m[i].Groups[0].ID.Hex()) } } // Look up for registerUrl. for i := range m { if len(m[i].Groups) == 0 { continue } if mHost, err := parseHost(m[i].RegisterURL); err == nil && mHost == host { return GetGroupById(m[i].Groups[0].ID.Hex()) } } } // KD does not have a jMachine document (TODO - #8514), instead // we return a group in a best-effor manner - we look up // the most recently accessed session for the user, // and give the the group attached to it. switch strings.ToLower(opts.Environment) { case "managed", "devmanaged": session, err := GetMostRecentSession(opts.Username) if err != nil { return nil, err } return GetGroup(session.GroupName) } return nil, mgo.ErrNotFound }
func (k *Klient) cmd(queryString, method, boxPath string) error { queryString, err := utils.QueryString(queryString) if err != nil { return err } k.Log.Debug("calling %q command on %q with %q", method, queryString, boxPath) kref, err := klient.ConnectTimeout(k.Kite, queryString, k.dialTimeout()) if err != nil { k.Log.Debug("connecting to %q klient failed: %s", queryString, err) return err } done := make(chan error, 1) success := dnode.Callback(func(*dnode.Partial) { done <- nil }) failure := dnode.Callback(func(r *dnode.Partial) { msg, err := r.One().String() if err != nil { err = errors.New("unknown failure") } else { err = errors.New(msg) } done <- err }) lost := make(chan struct{}) beat := make(chan struct{}) stop := make(chan struct{}) defer close(stop) go func() { // Heartbeat timer is initially stopped since at this // point we do not know whether klient supports heartbeats. // On first heartbeat we activate the timer and set the ch. var ( t *time.Timer ch <-chan time.Time ) for { select { case <-stop: return case <-ch: lost <- struct{}{} case <-beat: if t == nil { t = time.NewTimer(defaultDialTimeout) ch = t.C defer t.Stop() } t.Reset(defaultDialTimeout) } } }() heartbeat := dnode.Callback(func(r *dnode.Partial) { beat <- struct{}{} }) req := &Command{ FilePath: boxPath, Success: success, Failure: failure, Heartbeat: heartbeat, } if k.Debug { log := k.Log.New(method) req.Output = dnode.Callback(func(r *dnode.Partial) { log.Debug("%s", r.One().MustString()) }) } if _, err = kref.Client.TellWithTimeout(method, k.timeout(), req); err != nil { return errors.New("sending request to klient failed: " + err.Error()) } select { case err := <-done: return err case <-time.After(k.timeout()): return fmt.Errorf("timed out calling %q on %q", method, queryString) case <-lost: return errors.New("connection to your KD Daemon was lost due to inactivity") } }
// 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 }