// NewCheckUpdate is the required initializer for CheckUpdate. func NewCheckUpdate() *CheckUpdate { return &CheckUpdate{ LocalVersion: config.VersionNum(), Location: config.Konfig.Endpoints.KDLatest.Public.String(), RandomSeededNumber: rand.Intn(3), ForceCheck: false, } }
func (r *RunCommand) runOnRemote(localPath string, cmdWithArgsStr string) (*ExecRes, error) { machine, err := mountcli.NewMountcli().FindMountNameByPath(localPath) if err != nil { return nil, err } fullCmdPath, err := r.getCmdRemotePath(machine, localPath) if err != nil { return nil, err } // track metrics metrics.TrackRun(machine, config.VersionNum()) return r.runOnMachine(machine, fullCmdPath, cmdWithArgsStr) }
// Unmount tells klient to unmount the given name and path. func (c *UnmountCommand) Unmount(name, path string) error { req := req.UnmountFolder{ Name: name, LocalPath: path, } // currently there's no return response to care about if _, err := c.Klient.Tell("remote.unmountFolder", req); err != nil { return err } // track metrics metrics.TrackUnmount(name, config.VersionNum()) return nil }
// Run the Mount command func (c *MountCommand) Run() (int, error) { // Cleanup local folder when command exists with failure. cleanupPath := false defer func() { if cleanupPath { c.cleanupPath() } }() if c.Options.Debug { c.Log.SetLevel(logging.DEBUG) } // allow scp like declaration, ie `<machine name>:/path/to/remote` if strings.Contains(c.Options.Name, ":") { names := strings.SplitN(c.Options.Name, ":", 2) c.Options.Name, c.Options.RemotePath = names[0], names[1] } // setup our klient, if needed if exit, err := c.setupKlient(); err != nil { return exit, err } // Get the machine name from a partial name, if needed. if err := c.findMachineName(); err != nil { return 1, err } // Decide smart options, if needed, before the rest of // the flow. if err := c.smartOptions(); err != nil { return 1, err } if exit, err := c.handleOptions(); err != nil { return exit, err } // create the mount dir if needed if err := c.createMountDir(); err != nil { return 1, err } // If they choose not to use fuse, mount with only caching. if c.Options.OneWaySync { if err := c.useSync(); err != nil { cleanupPath = true return 1, err } } if c.Options.PrefetchAll { if err := c.prefetchAll(); err != nil { cleanupPath = true return 1, fmt.Errorf("failed to prefetch: %s", err) } } mountRequest := req.MountFolder{ Debug: c.Options.Debug, Name: c.Options.Name, LocalPath: c.Options.LocalPath, RemotePath: c.Options.RemotePath, NoIgnore: c.Options.NoIgnore, NoPrefetchMeta: c.Options.NoPrefetchMeta, PrefetchAll: c.Options.PrefetchAll, NoWatch: c.Options.NoWatch, CachePath: getCachePath(c.Options.Name), Trace: c.Options.Trace, OneWaySyncMount: c.Options.OneWaySync, } // Actually mount the folder. Errors are printed by the mountFolder func to the user. if err := c.mountFolder(mountRequest); err != nil { cleanupPath = true return 1, err } // track metrics o := map[string]interface{}{ "no-ignore": c.Options.NoIgnore, "no-prefetch-meta": c.Options.NoPrefetchMeta, "prefetch-all": c.Options.PrefetchAll, "oneway-sync": c.Options.OneWaySync, "no-watch": c.Options.NoWatch, "version": config.VersionNum(), } metrics.TrackMount(c.Options.Name, c.Options.LocalPath, o) c.printfln("Mount complete.") return 0, nil }
// The implementation of InstallCommandFactory, with an error return. This // allows us to track the error metrics. func InstallCommandFactory(c *cli.Context, log logging.Logger, _ string) (exit int, err error) { if len(c.Args()) != 1 { cli.ShowCommandHelp(c, "install") return 1, errors.New("incorrect cli usage: no args") } // Now that we created the logfile, set our logger handler to use that newly created // file, so that we can log errors during installation. f, err := createLogFile(LogFilePath) if err != nil { fmt.Println(`Error: Unable to open log files.`) } else { log.SetHandler(logging.NewWriterHandler(f)) log.Info("Installation created log file at %q", LogFilePath) } // Track all failed installations. defer func() { if err != nil { log.Error(err.Error()) metrics.TrackInstallFailed(err.Error(), config.VersionNum()) } }() authToken := c.Args().Get(0) // We need to check if the authToken is somehow empty, because klient // will default to user/pass if there is no auth token (despite setting // the token flag) if strings.TrimSpace(authToken) == "" { cli.ShowCommandHelp(c, "install") return 1, errors.New("incorrect cli usage: missing token") } // Create the installation dir, if needed. if err := os.MkdirAll(KlientDirectory, 0755); err != nil { log.Error( "Error creating klient binary directory(s). path:%s, err:%s", KlientDirectory, err, ) fmt.Println(FailedInstallingKlient) return 1, fmt.Errorf("failed creating klient binary: %s", err) } klientBinPath := filepath.Join(KlientDirectory, "klient") // TODO: Accept `kd install --user foo` flag to replace the // environ checking. klientSh := klientSh{ User: konfig.CurrentUser.Username, KlientBinPath: klientBinPath, } if err := klientSh.Create(filepath.Join(KlientDirectory, "klient.sh")); err != nil { err = fmt.Errorf("error writing klient.sh file: %s", err) fmt.Println(FailedInstallingKlient) return 1, err } fmt.Println("Downloading...") version, err := latestVersion(config.Konfig.Endpoints.KlientLatest.Public.String()) if err != nil { fmt.Printf(FailedDownloadingKlient) return 1, fmt.Errorf("error getting latest klient version: %s", err) } if err := downloadRemoteToLocal(config.S3Klient(version, config.Environment), klientBinPath); err != nil { fmt.Printf(FailedDownloadingKlient) return 1, fmt.Errorf("error downloading klient binary: %s", err) } fmt.Printf("Created %s\n", klientBinPath) fmt.Printf(`Authenticating you to the %s `, config.KlientName) cmd := exec.Command(klientBinPath, "-register", "-token", authToken, "--kontrol-url", strings.TrimSpace(c.String("kontrol")), ) var errBuf bytes.Buffer // Note that we are *only* printing to Stdout. This is done because // Klient logs error messages to Stderr, and we want to control the UX for // that interaction. // // TODO: Logg Klient's Stderr message on error, if any. cmd.Stdout = os.Stdout cmd.Stdin = os.Stdin cmd.Stderr = &errBuf if err := cmd.Run(); err != nil { err = fmt.Errorf("error registering klient: %s, klient stderr: %s", err, errBuf.String()) fmt.Println(FailedRegisteringKlient) return 1, err } // Best-effort attempts at fixinig permissions and ownership, ignore any errors. _ = configstore.FixOwner() opts := &ServiceOptions{ Username: klientSh.User, } // Create our interface to the OS specific service s, err := newService(opts) if err != nil { fmt.Println(GenericInternalNewCodeError) return 1, fmt.Errorf("error creating Service: %s", err) } // try to uninstall first, otherwise Install may fail if // klient.plist or klient init script already exist s.Uninstall() // Install the klient binary as a OS service if err := s.Install(); err != nil { fmt.Println(GenericInternalNewCodeError) return 1, fmt.Errorf("error installing Service: %s", err) } // Tell the service to start. Normally it starts automatically, but // if the user told the service to stop (previously), it may not // start automatically. // // Note that the service may error if it is already running, so // we're ignoring any starting errors here. We will verify the // connection below, anyway. if err := s.Start(); err != nil { fmt.Println(FailedStartKlient) return 1, fmt.Errorf("error starting klient service: %s", err) } fmt.Println("Verifying installation...") err = WaitUntilStarted(config.Konfig.Endpoints.Klient.Private.String(), CommandAttempts, CommandWaitTime) // After X times, if err != nil we failed to connect to klient. // Inform the user. if err != nil { fmt.Println(FailedInstallingKlient) return 1, fmt.Errorf("error verifying the installation of klient: %s", err) } // Best-effort attempts at fixinig permissions and ownership, ignore any errors. _ = uploader.FixPerms() // track metrics metrics.TrackInstall(config.VersionNum()) fmt.Printf("\n\nSuccessfully installed and started the %s!\n", config.KlientName) return 0, nil }
// SSHCommandFactory is the factory method for SSHCommand. func SSHCommandFactory(c *cli.Context, log logging.Logger, _ string) int { if len(c.Args()) != 1 { cli.ShowCommandHelp(c, "ssh") return 1 } if c.Bool("debug") { log.SetLevel(logging.DEBUG) } opts := ssh.SSHCommandOpts{ Debug: c.Bool("debug") || config.Konfig.Debug, RemoteUsername: c.String("username"), Ask: true, } cmd, err := ssh.NewSSHCommand(log, opts) mountName := c.Args()[0] // TODO: Refactor SSHCommand instance to require no initialization, // and thus avoid needing to log an error in a weird place. if err != nil { log.Error("Error initializing ssh: %s", err) switch err { case ssh.ErrLocalDialingFailed: fmt.Println( defaultHealthChecker.CheckAllFailureOrMessagef(KlientIsntRunning), ) default: fmt.Println(GenericInternalError) } metrics.TrackSSHFailed(mountName, err.Error(), config.VersionNum()) return 1 } now := time.Now() // track metrics go func() { metrics.TrackSSH(mountName, config.VersionNum()) }() err = cmd.Run(mountName) switch err { case nil: metrics.TrackSSHEnd(mountName, "", -now.Sub(now).Minutes(), config.VersionNum()) return 0 case ssh.ErrMachineNotFound: fmt.Println(MachineNotFound) case ssh.ErrCannotFindUser: fmt.Println(CannotFindSSHUser) metrics.TrackSSHFailed(mountName, err.Error(), config.VersionNum()) case ssh.ErrFailedToGetSSHKey: fmt.Println(FailedGetSSHKey) metrics.TrackSSHFailed(mountName, err.Error(), config.VersionNum()) case ssh.ErrMachineNotValidYet: fmt.Println(defaultHealthChecker.CheckAllFailureOrMessagef(MachineNotValidYet)) metrics.TrackSSHFailed(mountName, err.Error(), config.VersionNum()) case ssh.ErrRemoteDialingFailed: fmt.Println(defaultHealthChecker.CheckAllFailureOrMessagef(FailedDialingRemote)) metrics.TrackSSHFailed(mountName, err.Error(), config.VersionNum()) case shortcut.ErrMachineNotFound: fmt.Println(MachineNotFound) metrics.TrackSSHFailed(mountName, err.Error(), config.VersionNum()) } log.Error("SSHCommand.Run returned err:%s", err) // ssh returns `exit status 255` on disconnection; so we also send how long // session has been running for to indicate if ssh was successful at least // once and the failed due to disconnection metrics.TrackSSHEnd(mountName, err.Error(), -now.Sub(now).Minutes(), config.VersionNum()) return 1 }