// TrackFile will track an individual file, meaning, it will move the original // file to either the files or backup folder. It will the create a symlink of // the file in the original location. `name` will be used as the name of the // folder and key in the config file. `fullPath` has to be the absolute path // to the file to be tracked. // // TrackFile can be called from two contexes: // 1. From SyncFiles, it will read all the tracked files from the config and // make track files if necessary. // 2. From CommandAdd, this will add a new file for tracking // // TrackFile will make a distinction between a new file and a file that is // already been tracked: // 1. TrackFile can't find the symlink, but the file is present in the // dot_path (the folder that holds all the original files). Then we need to // relink it, thus creating a symlink at the correct location. This happens // we you run dot on a new 'additional machine'. // 2. TrackFile can't find the symlink, and the file is also not present in // the dot_path folder. This will mean that it is a new file were are going // to track. So we copy the file to the files folder, create a symlink, and // add an entry to the config file. func TrackFile(name string, fullPath string, push bool) { // Base base := path.Base(fullPath) // get relative path relPath, err := GetRelativePath(fullPath) if err != nil { PrintBodyError(err.Error()) return } // check if path is present _, err = os.Stat(fullPath) if err != nil { message := fmt.Sprintf("not able to find: %s", fullPath) PrintBodyError(message) return } // check if path is already symlinked s, err := os.Lstat(fullPath) if s.Mode()&os.ModeSymlink == os.ModeSymlink { message := fmt.Sprintf("%s is already symlinked, skipping ...", name) PrintBody(message) return } // load config c, err := NewConfig(PathDotConfig) if err != nil { PrintBodyError("not able to find .dotconfig") return } repoPath := fmt.Sprintf("%s%s/files/%s/", HomeDir(), c.DotPath, name) if _, err := os.Stat(repoPath); err == nil { // no symlink found, already in repo => additional machine message := fmt.Sprintf("Symlinking: %s", name) PrintBody(message) // put in backup folder, set named folder based on `name`, e.g.: // `/home/erroneousboat/dotfiles/backup/[name]/[base]` dst := fmt.Sprintf("%s%s/backup/%s/%s", HomeDir(), c.DotPath, name, base) err = MakeAndMoveToDir(fullPath, dst) if err != nil { log.Fatal(err) } // create symlink (os.Symlink(oldname, newname)) dst = fmt.Sprintf("%s%s/files/%s/%s", HomeDir(), c.DotPath, name, base) err = os.Symlink(dst, fullPath) if err != nil { log.Fatal(err) } } else { // no symlink found, not in repo => new entry message := fmt.Sprintf("Symlinking: %s", name) PrintBody(message) // put in files folder, set named folder based on `name`, e.g.: // `/home/erroneousboat/dotfiles/files/[name]/[base]` dst := fmt.Sprintf("%s%s/files/%s/%s", HomeDir(), c.DotPath, name, base) err = MakeAndMoveToDir(fullPath, dst) if err != nil { log.Fatal(err) } // create symlink (os.Symlink(oldname, newname)) err = os.Symlink(dst, fullPath) if err != nil { log.Fatal(err) } // create entry in .dotconfig file c.Files[name] = relPath c.Save() // push changes to repository if push { GitCommitPush(name, "add") } } }
func Main() { userset := flag.NewFlagSet("fleet", flag.ExitOnError) printVersion := userset.Bool("version", false, "Print the version and exit") cfgPath := userset.String("config", "", fmt.Sprintf("Path to config file. Fleet will look for a config at %s by default.", DefaultConfigFile)) userset.Usage = func() { fmt.Fprintf(os.Stderr, "%s\nUsage of %s:\n", FleetdDescription, os.Args[0]) userset.PrintDefaults() } err := userset.Parse(os.Args[1:]) if err != nil { userset.Usage() os.Exit(1) } args := userset.Args() if len(args) > 0 { // support `fleetd version` the same as `fleetd --version` if args[0] == "version" { *printVersion = true } else { fmt.Fprintf(os.Stderr, "%s takes no arguments. Did you mean to invoke fleetctl instead?\n", os.Args[0]) userset.Usage() os.Exit(1) } } if *printVersion { fmt.Println("fleetd version", version.Version) os.Exit(0) } log.Infof("Starting fleetd version %v", version.Version) cfgset := flag.NewFlagSet("fleet", flag.ExitOnError) cfgset.Int("verbosity", 0, "Logging level") cfgset.Var(&pkg.StringSlice{"http://127.0.0.1:2379", "http://127.0.0.1:4001"}, "etcd_servers", "List of etcd endpoints") cfgset.String("etcd_username", "", "username for secure etcd communication") cfgset.String("etcd_password", "", "password for secure etcd communication") cfgset.String("etcd_keyfile", "", "SSL key file used to secure etcd communication") cfgset.String("etcd_certfile", "", "SSL certification file used to secure etcd communication") cfgset.String("etcd_cafile", "", "SSL Certificate Authority file used to secure etcd communication") cfgset.String("etcd_key_prefix", registry.DefaultKeyPrefix, "Keyspace for fleet data in etcd") cfgset.Float64("etcd_request_timeout", 1.0, "Amount of time in seconds to allow a single etcd request before considering it failed.") cfgset.Float64("engine_reconcile_interval", 2.0, "Interval at which the engine should reconcile the cluster schedule in etcd.") cfgset.String("public_ip", "", "IP address that fleet machine should publish") cfgset.String("metadata", "", "List of key-value metadata to assign to the fleet machine") cfgset.String("agent_ttl", agent.DefaultTTL, "TTL in seconds of fleet machine state in etcd") cfgset.String("units_directory", "/run/fleet/units/", "Path to the fleet units directory") cfgset.Bool("systemd_user", false, "When true use systemd --user)") cfgset.Int("token_limit", 100, "Maximum number of entries per page returned from API requests") cfgset.Bool("enable_grpc", false, "When possible, uses grpc to communicate between engine and agent") cfgset.Bool("disable_engine", false, "Disable the engine entirely, use with care") cfgset.Bool("disable_watches", false, "Disable the use of etcd watches. Increases scheduling latency") cfgset.Bool("verify_units", false, "DEPRECATED - This option is ignored") cfgset.String("authorized_keys_file", "", "DEPRECATED - This option is ignored") globalconf.Register("", cfgset) cfg, err := getConfig(cfgset, *cfgPath) if err != nil { log.Fatal(err) } log.Debugf("Creating Server") srv, err := server.New(*cfg, nil) if err != nil { log.Fatalf("Failed creating Server: %v", err) } srv.Run() srvMutex := sync.Mutex{} reconfigure := func() { log.Infof("Reloading configuration from %s", *cfgPath) srvMutex.Lock() defer srvMutex.Unlock() cfg, err := getConfig(cfgset, *cfgPath) if err != nil { log.Fatal(err) } log.Infof("Restarting server components") srv.SetReconfigServer(true) // Get Server.listeners[] to keep it for a new server, // before killing the old server. oldListeners := srv.GetApiServerListeners() srv.Kill() // The new server takes the original listeners. srv, err = server.New(*cfg, oldListeners) if err != nil { log.Fatal(err) } srv.Run() srv.SetReconfigServer(false) } shutdown := func() { log.Infof("Gracefully shutting down") srvMutex.Lock() defer srvMutex.Unlock() srv.Kill() srv.Purge() os.Exit(0) } writeState := func() { log.Infof("Dumping server state") srvMutex.Lock() defer srvMutex.Unlock() encoded, err := json.Marshal(srv) if err != nil { log.Errorf("Failed to dump server state: %v", err) return } if _, err := os.Stdout.Write(encoded); err != nil { log.Errorf("Failed to dump server state: %v", err) return } os.Stdout.Write([]byte("\n")) log.Debugf("Finished dumping server state") } signals := map[os.Signal]func(){ syscall.SIGHUP: reconfigure, syscall.SIGTERM: shutdown, syscall.SIGINT: shutdown, syscall.SIGUSR1: writeState, } listenForSignals(signals) }
// UntrackFile will remove a file from tracking. `name` will be the key // in the config file that points to the initial location of the file func UntrackFile(name string, push bool) { // open config file c, err := NewConfig(HomeDir() + "/" + ConfigFileName) if err != nil { PrintBodyError("not able to find .dotconfig") return } // check if `name` is present in c.Files path := c.Files[name] if path == "" { message := fmt.Sprintf("'%s' is not being tracked. Get the list of "+ "tracked files with `dot list`", name) PrintBodyError(message) return } // check if path (the symlink) is present pathSymlink := fmt.Sprintf("%s%s", HomeDir(), path) f, err := os.Lstat(pathSymlink) if err != nil { message := fmt.Sprintf("not able to find: %s", path) PrintBodyError(message) return } // check if path is symlink if f.Mode()&os.ModeSymlink != os.ModeSymlink { message := fmt.Sprintf("%s is not a symlink", path) PrintBodyError(message) return } // check if src is present src := fmt.Sprintf("%s%s/files/%s", HomeDir(), c.DotPath, name) if _, err = os.Stat(src); err != nil { message := fmt.Sprintf("not able to find %s", src) PrintBodyError(message) return } // remove symlink err = os.Remove(pathSymlink) if err != nil { message := fmt.Sprintf("not able to remove %s", pathSymlink) PrintBodyError(message) return } // move the file or directory dst := fmt.Sprintf("%s%s", HomeDir(), path) message := fmt.Sprintf("Moving %s back to %s", name, dst) PrintBody(message) err = MakeAndMoveToDir(src, dst) if err != nil { log.Fatal(err) } // remove entry from config and save config delete(c.Files, name) c.Save() // push changes to repository if push { GitCommitPush(name, "rm") } }