func main() { if err := run(); err != nil { // The action we take depends on the error we get. switch t := err.(type) { case *url.Error: shared.Debugf("url.Error caught in main(). Op: %s, URL: %s, Err: %s\n", t.Op, t.URL, t.Err) switch u := t.Err.(type) { case *net.OpError: shared.Debugf("Inner error type is a net.OpError: Op: %s Net: %s Addr: %s Err: %T", u.Op, u.Net, u.Addr, u.Err) if u.Op == "dial" && u.Net == "unix" { // The unix socket we are trying to conect to is refusing our connection attempt. Perhaps the server is not running? // Let's at least tell the user about it, since it's hard to get information on wether something is actually listening. fmt.Fprintf(os.Stderr, fmt.Sprintf(gettext.Gettext("Cannot connect to unix socket at %s Is the server running?\n"), u.Addr)) os.Exit(1) } default: shared.Debugf("url.Error's inner Err type is %T", u) } default: shared.Debugf("Error caught in main: %T\n", t) } fmt.Fprintf(os.Stderr, gettext.Gettext("error: %v\n"), err) os.Exit(1) } }
func (c *Client) delete(base string, args shared.Jmap, rtype ResponseType) (*Response, error) { uri := c.url(shared.APIVersion, base) buf := bytes.Buffer{} err := json.NewEncoder(&buf).Encode(args) if err != nil { return nil, err } shared.Debugf("deleting %s to %s", buf.String(), uri) req, err := http.NewRequest("DELETE", uri, &buf) if err != nil { return nil, err } req.Header.Set("User-Agent", shared.UserAgent) req.Header.Set("Content-Type", "application/json") resp, err := c.http.Do(req) if err != nil { return nil, err } return HoistResponse(resp, rtype) }
/* * load the server cert from disk */ func (c *Client) loadServerCert() { cert, err := shared.ReadCert(ServerCertPath(c.name)) if err != nil { shared.Debugf("Error reading the server certificate for %s: %v\n", c.name, err) return } c.scert = cert }
func (c *Client) Finger() error { shared.Debugf("fingering the daemon") resp, err := c.GetServerConfig() if err != nil { return err } jmap, err := resp.MetadataAsMap() if err != nil { return err } serverAPICompat, err := jmap.GetInt("api_compat") if err != nil { return err } if serverAPICompat != shared.APICompat { return fmt.Errorf(gettext.Gettext("api version mismatch: mine: %q, daemon: %q"), shared.APICompat, serverAPICompat) } shared.Debugf("pong received") return nil }
func (c *Client) SetProfileConfigItem(profile, key, value string) error { st, err := c.ProfileConfig(profile) if err != nil { shared.Debugf("Error getting profile %s to update\n", profile) return err } if value == "" { delete(st.Config, key) } else { st.Config[key] = value } body := shared.Jmap{"name": profile, "config": st.Config, "devices": st.Devices} _, err = c.put(fmt.Sprintf("profiles/%s", profile), body, Sync) return err }
/* Wait for an operation */ func (c *Client) WaitFor(waitURL string) (*shared.Operation, error) { if len(waitURL) < 1 { return nil, fmt.Errorf(gettext.Gettext("invalid wait url %s"), waitURL) } /* For convenience, waitURL is expected to be in the form of a * Response.Operation string, i.e. it already has * "/<version>/operations/" in it; we chop off the leading / and pass * it to url directly. */ shared.Debugf(path.Join(waitURL[1:], "wait")) resp, err := c.baseGet(c.url(waitURL, "wait")) if err != nil { return nil, err } return resp.MetadataAsOperation() }
// ParseResponse parses a lxd style response out of an http.Response. Note that // this does _not_ automatically convert error responses to golang errors. To // do that, use ParseError. Internal client library uses should probably use // HoistResponse, unless they are interested in accessing the underlying Error // response (e.g. to inspect the error code). func ParseResponse(r *http.Response) (*Response, error) { if r == nil { return nil, fmt.Errorf(gettext.Gettext("no response!")) } defer r.Body.Close() ret := Response{} s, err := ioutil.ReadAll(r.Body) if err != nil { return nil, err } shared.Debugf("raw response: %s", string(s)) if err := json.Unmarshal(s, &ret); err != nil { return nil, err } return &ret, nil }
func (c *Client) AmTrusted() bool { resp, err := c.GetServerConfig() if err != nil { return false } shared.Debugf("%s", resp) jmap, err := resp.MetadataAsMap() if err != nil { return false } auth, err := jmap.GetString("auth") if err != nil { return false } return auth == "trusted" }
func removeCertificate(remote string) { certf := lxd.ServerCertPath(remote) shared.Debugf("Trying to remove %s\n", certf) os.Remove(certf) }
func (c *Client) Exec(name string, cmd []string, env map[string]string, stdin *os.File, stdout *os.File, stderr *os.File) (int, error) { interactive := terminal.IsTerminal(int(stdin.Fd())) body := shared.Jmap{"command": cmd, "wait-for-websocket": true, "interactive": interactive, "environment": env} resp, err := c.post(fmt.Sprintf("containers/%s/exec", name), body, Async) if err != nil { return -1, err } md := execMd{} if err := json.Unmarshal(resp.Metadata, &md); err != nil { return -1, err } if interactive { if wsControl, ok := md.FDs["control"]; ok { go func() { control, err := c.websocket(resp.Operation, wsControl) if err != nil { return } for { width, height, err := terminal.GetSize(syscall.Stdout) if err != nil { continue } shared.Debugf("Window size is now: %dx%d\n", width, height) w, err := control.NextWriter(websocket.TextMessage) if err != nil { shared.Debugf("got error getting next writer %s", err) break } msg := shared.ContainerExecControl{} msg.Command = "window-resize" msg.Args = make(map[string]string) msg.Args["width"] = strconv.Itoa(width) msg.Args["height"] = strconv.Itoa(height) buf, err := json.Marshal(msg) if err != nil { shared.Debugf("failed to convert to json %s", err) break } _, err = w.Write(buf) w.Close() if err != nil { shared.Debugf("got err writing %s", err) break } ch := make(chan os.Signal) signal.Notify(ch, syscall.SIGWINCH) sig := <-ch shared.Debugf("Received '%s signal', updating window geometry.\n", sig) } closeMsg := websocket.FormatCloseMessage(websocket.CloseNormalClosure, "") control.WriteMessage(websocket.CloseMessage, closeMsg) }() } conn, err := c.websocket(resp.Operation, md.FDs["0"]) if err != nil { return -1, err } shared.WebsocketSendStream(conn, stdin) <-shared.WebsocketRecvStream(stdout, conn) } else { sources := []*os.File{stdin, stdout, stderr} conns := make([]*websocket.Conn, 3) dones := make([]chan bool, 3) for i := 0; i < 3; i++ { conns[i], err = c.websocket(resp.Operation, md.FDs[strconv.Itoa(i)]) if err != nil { return -1, err } if i == 0 { dones[i] = shared.WebsocketSendStream(conns[i], sources[i]) } else { dones[i] = shared.WebsocketRecvStream(sources[i], conns[i]) } } /* * We'll get a read signal from each of stdout, stderr when they've * both died. We need to wait for these in addition to the operation, * because the server may indicate that the operation is done before we * can actually read the last bits of data off these sockets and print * it to the screen. * * We don't wait for stdin here, because if we're interactive, the user * may not have closed it (e.g. if the command exits but the user * didn't ^D). */ for i := 1; i < 3; i++ { <-dones[i] } // Once we're done, we explicitly close stdin, to signal the websockets // we're done. sources[0].Close() } // Now, get the operation's status too. op, err := c.WaitFor(resp.Operation) if err != nil { return -1, err } if op.StatusCode == shared.Failure { return -1, op.GetError() } if op.StatusCode != shared.Success { return -1, fmt.Errorf(gettext.Gettext("got bad op status %s"), op.Status) } opMd, err := op.MetadataAsMap() if err != nil { return -1, err } return opMd.GetInt("return") }