in cmd/legacy_main.go [252:455]
func Mount(newConfig *cfg.Config, bucketName, mountPoint string) (err error) {
// Ideally this call to SetLogFormat (which internally creates a new defaultLogger)
// should be set as an else to the 'if flags.Foreground' check below, but currently
// that means the logs generated by resolveConfigFilePaths below don't honour
// the user-provided log-format.
logger.SetLogFormat(newConfig.Logging.Format)
if newConfig.Foreground {
err = logger.InitLogFile(newConfig.Logging)
if err != nil {
return fmt.Errorf("init log file: %w", err)
}
}
logger.Infof("Start gcsfuse/%s for app %q using mount point: %s\n", common.GetVersion(), newConfig.AppName, mountPoint)
// Log mount-config and the CLI flags in the log-file.
// If there is no log-file, then log these to stdout.
// Do not log these in stdout in case of daemonized run
// if these are already being logged into a log-file, otherwise
// there will be duplicate logs for these in both places (stdout and log-file).
if newConfig.Foreground || newConfig.Logging.FilePath == "" {
logger.Info("GCSFuse config", "config", newConfig)
}
// The following will not warn if the user explicitly passed the default value for StatCacheCapacity.
if newConfig.MetadataCache.DeprecatedStatCacheCapacity != mount.DefaultStatCacheCapacity {
logger.Warnf("Deprecated flag stat-cache-capacity used! Please switch to config parameter 'metadata-cache: stat-cache-max-size-mb'.")
}
// The following will not warn if the user explicitly passed the default value for StatCacheTTL or TypeCacheTTL.
if newConfig.MetadataCache.DeprecatedStatCacheTtl != mount.DefaultStatOrTypeCacheTTL || newConfig.MetadataCache.DeprecatedTypeCacheTtl != mount.DefaultStatOrTypeCacheTTL {
logger.Warnf("Deprecated flag stat-cache-ttl and/or type-cache-ttl used! Please switch to config parameter 'metadata-cache: ttl-secs' .")
}
// If we haven't been asked to run in foreground mode, we should run a daemon
// with the foreground flag set and wait for it to mount.
if !newConfig.Foreground {
// Find the executable.
var path string
path, err = osext.Executable()
if err != nil {
err = fmt.Errorf("osext.Executable: %w", err)
return
}
// Set up arguments. Be sure to use foreground mode, and to send along the
// potentially-modified mount point.
args := append([]string{"--foreground"}, os.Args[1:]...)
args[len(args)-1] = mountPoint
// Pass along PATH so that the daemon can find fusermount on Linux.
env := []string{
fmt.Sprintf("PATH=%s", os.Getenv("PATH")),
}
// Pass along GOOGLE_APPLICATION_CREDENTIALS, since we document in
// mounting.md that it can be used for specifying a key file.
if p, ok := os.LookupEnv("GOOGLE_APPLICATION_CREDENTIALS"); ok {
env = append(env, fmt.Sprintf("GOOGLE_APPLICATION_CREDENTIALS=%s", p))
}
// Pass through the https_proxy/http_proxy environment variable,
// in case the host requires a proxy server to reach the GCS endpoint.
// https_proxy has precedence over http_proxy, in case both are set
if p, ok := os.LookupEnv("https_proxy"); ok {
env = append(env, fmt.Sprintf("https_proxy=%s", p))
fmt.Fprintf(
os.Stdout,
"Added environment https_proxy: %s\n",
p)
} else if p, ok := os.LookupEnv("http_proxy"); ok {
env = append(env, fmt.Sprintf("http_proxy=%s", p))
fmt.Fprintf(
os.Stdout,
"Added environment http_proxy: %s\n",
p)
}
// Pass through the no_proxy environment variable. Whenever
// using the http(s)_proxy environment variables. This should
// also be included to know for which hosts the use of proxies
// should be ignored.
if p, ok := os.LookupEnv("no_proxy"); ok {
env = append(env, fmt.Sprintf("no_proxy=%s", p))
fmt.Fprintf(
os.Stdout,
"Added environment no_proxy: %s\n",
p)
}
// Pass the parent process working directory to child process via
// environment variable. This variable will be used to resolve relative paths.
if parentProcessExecutionDir, err := os.Getwd(); err == nil {
env = append(env, fmt.Sprintf("%s=%s", util.GCSFUSE_PARENT_PROCESS_DIR,
parentProcessExecutionDir))
}
// Here, parent process doesn't pass the $HOME to child process implicitly,
// hence we need to pass it explicitly.
if homeDir, _ := os.UserHomeDir(); err == nil {
env = append(env, fmt.Sprintf("HOME=%s", homeDir))
}
// This environment variable will be helpful to distinguish b/w the main
// process and daemon process. If this environment variable set that means
// programme is running as daemon process.
env = append(env, fmt.Sprintf("%s=true", logger.GCSFuseInBackgroundMode))
// logfile.stderr will capture the standard error (stderr) output of the gcsfuse background process.
var stderrFile *os.File
if newConfig.Logging.FilePath != "" {
stderrFileName := string(newConfig.Logging.FilePath) + ".stderr"
if stderrFile, err = os.OpenFile(stderrFileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644); err != nil {
return err
}
}
// Run.
err = daemonize.Run(path, args, env, os.Stdout, stderrFile)
if err != nil {
return fmt.Errorf("daemonize.Run: %w", err)
}
logger.Infof(SuccessfulMountMessage)
return err
}
ctx := context.Background()
var metricExporterShutdownFn common.ShutdownFn
metricHandle := common.NewNoopMetrics()
if cfg.IsMetricsEnabled(&newConfig.Metrics) {
metricExporterShutdownFn = monitor.SetupOTelMetricExporters(ctx, newConfig)
if metricHandle, err = common.NewOTelMetrics(); err != nil {
metricHandle = common.NewNoopMetrics()
}
}
shutdownTracingFn := monitor.SetupTracing(ctx, newConfig)
shutdownFn := common.JoinShutdownFunc(metricExporterShutdownFn, shutdownTracingFn)
// Mount, writing information about our progress to the writer that package
// daemonize gives us and telling it about the outcome.
var mfs *fuse.MountedFileSystem
{
mfs, err = mountWithArgs(bucketName, mountPoint, newConfig, metricHandle)
// This utility is to absorb the error
// returned by daemonize.SignalOutcome calls by simply
// logging them as error logs.
callDaemonizeSignalOutcome := func(err error) {
if err2 := daemonize.SignalOutcome(err); err2 != nil {
logger.Errorf("Failed to signal error to parent-process from daemon: %v", err2)
}
}
markSuccessfulMount := func() {
// Print the success message in the log-file/stdout depending on what the logger is set to.
logger.Info(SuccessfulMountMessage)
callDaemonizeSignalOutcome(nil)
}
markMountFailure := func(err error) {
// Printing via mountStatus will have duplicate logs on the console while
// mounting gcsfuse in foreground mode. But this is important to avoid
// losing error logs when run in the background mode.
logger.Errorf("%s: %v\n", UnsuccessfulMountMessagePrefix, err)
err = fmt.Errorf("%s: mountWithArgs: %w", UnsuccessfulMountMessagePrefix, err)
callDaemonizeSignalOutcome(err)
}
if err != nil {
markMountFailure(err)
return err
}
if !isDynamicMount(bucketName) {
switch newConfig.MetadataCache.ExperimentalMetadataPrefetchOnMount {
case cfg.ExperimentalMetadataPrefetchOnMountSynchronous:
if err = callListRecursive(mountPoint); err != nil {
markMountFailure(err)
return err
}
case cfg.ExperimentalMetadataPrefetchOnMountAsynchronous:
go func() {
if err := callListRecursive(mountPoint); err != nil {
logger.Errorf("Metadata-prefetch failed: %v", err)
}
}()
}
}
markSuccessfulMount()
}
// Let the user unmount with Ctrl-C (SIGINT).
registerTerminatingSignalHandler(mfs.Dir(), newConfig)
// Wait for the file system to be unmounted.
if err = mfs.Join(ctx); err != nil {
err = fmt.Errorf("MountedFileSystem.Join: %w", err)
}
if shutdownFn != nil {
if shutdownErr := shutdownFn(ctx); shutdownErr != nil {
logger.Errorf("Error while shutting down trace exporter: %v", shutdownErr)
}
}
return err
}