azure-storage-fuse/cmd/mountv1.go

564 строки
24 KiB
Go
Executable File

/*
_____ _____ _____ ____ ______ _____ ------
| | | | | | | | | | | | |
| | | | | | | | | | | | |
| --- | | | | |-----| |---- | | |-----| |----- ------
| | | | | | | | | | | | |
| ____| |_____ | ____| | ____| | |_____| _____| |_____ |_____
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
Copyright © 2020-2023 Microsoft Corporation. All rights reserved.
Author : <blobfusedev@microsoft.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
*/
package cmd
import (
"bufio"
"bytes"
"errors"
"fmt"
"log/syslog"
"os"
"strconv"
"strings"
"github.com/Azure/azure-storage-fuse/v2/common"
"github.com/Azure/azure-storage-fuse/v2/common/log"
"github.com/Azure/azure-storage-fuse/v2/component/attr_cache"
"github.com/Azure/azure-storage-fuse/v2/component/azstorage"
"github.com/Azure/azure-storage-fuse/v2/component/file_cache"
"github.com/Azure/azure-storage-fuse/v2/component/libfuse"
"github.com/Azure/azure-storage-fuse/v2/component/stream"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"gopkg.in/yaml.v3"
)
type blobfuseCliOptions struct {
configFile string
logLevel string
fuseLogging bool
useAttrCache bool
useStreaming bool
// fuseAttrTimeout uint32
// fuseEntryTimeout uint32
tmpPath string
cacheSize float64
fileCacheTimeout uint32
maxEviciton uint32
highDiskThreshold uint32
lowDiskThreshold uint32
emptyDirCheck bool
blockSize uint64
maxBlocksPerFile int
streamCacheSize uint64
noSymlinks bool
cacheOnList bool
useAdls bool
useHttps bool
containerName string
maxConcurrency uint16
cancelListOnMount uint16
maxRetry int32
maxRetryInterval int32
retryDelayFactor int32
httpProxy string
httpsProxy string
ignoreOpenFlags bool
}
type ComponentsConfig []string
type PipelineConfig struct {
ForegroundOption bool `yaml:"foreground,omitempty"`
ReadOnlyOption bool `yaml:"read-only,omitempty"`
AllowOtherOption bool `yaml:"allow-other,omitempty"`
NonEmptyMountOption bool `yaml:"nonempty,omitempty"`
LogOptions `yaml:"logging,omitempty"`
libfuse.LibfuseOptions `yaml:"libfuse,omitempty"`
stream.StreamOptions `yaml:"stream,omitempty"`
file_cache.FileCacheOptions `yaml:"file_cache,omitempty"`
attr_cache.AttrCacheOptions `yaml:"attr_cache,omitempty"`
azstorage.AzStorageOptions `yaml:"azstorage,omitempty"`
ComponentsConfig `yaml:"components,omitempty"`
}
var outputFilePath string
var mountPath string
var libfuseOptions []string
var bfConfCliOptions blobfuseCliOptions
var bfv2StorageConfigOptions azstorage.AzStorageOptions
var bfv2LoggingConfigOptions LogOptions
var bfv2FuseConfigOptions libfuse.LibfuseOptions
var bfv2FileCacheConfigOptions file_cache.FileCacheOptions
var bfv2AttrCacheConfigOptions attr_cache.AttrCacheOptions
var bfv2ComponentsConfigOptions ComponentsConfig
var bfv2StreamConfigOptions stream.StreamOptions
var bfv2ForegroundOption bool
var bfv2ReadOnlyOption bool
var bfv2NonEmptyMountOption bool
var bfv2AllowOtherOption bool
var useAttrCache bool
var useStream bool
var useFileCache bool = true
var convertConfigOnly bool
var enableGen1 bool
var reqFreeSpaceMB int
func resetOptions() {
bfv2StorageConfigOptions = azstorage.AzStorageOptions{}
bfv2LoggingConfigOptions = LogOptions{}
bfv2FuseConfigOptions = libfuse.LibfuseOptions{}
bfv2FileCacheConfigOptions = file_cache.FileCacheOptions{}
bfv2AttrCacheConfigOptions = attr_cache.AttrCacheOptions{}
bfv2ComponentsConfigOptions = ComponentsConfig{}
bfv2StreamConfigOptions = stream.StreamOptions{}
bfv2ForegroundOption = false
bfv2ReadOnlyOption = false
bfv2NonEmptyMountOption = false
bfv2AllowOtherOption = false
useAttrCache = false
useStream = false
useFileCache = true
}
var generateConfigCmd = &cobra.Command{
Use: "mountv1",
Short: "Generate a configuration file for Blobfuse2 from Blobfuse configuration file/flags",
Long: "Generate a configuration file for Blobfuse2 from Blobfuse configuration file/flags",
SuggestFor: []string{"conv config", "convert config"},
Args: cobra.MaximumNArgs(1),
FlagErrorHandling: cobra.ExitOnError,
RunE: func(cmd *cobra.Command, args []string) error {
if !disableVersionCheck {
err := VersionCheck()
if err != nil {
log.Err(err.Error())
}
}
resetOptions()
// If we are only converting the config without mounting then we do not need the mount path and therefore the args length would be 0
if len(args) == 1 {
mountPath = args[0]
}
file, err := os.Open(bfConfCliOptions.configFile)
if err == nil {
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
// some users may have a commented out config
linePieces := strings.SplitN(scanner.Text(), "#", 2)
line := linePieces[0]
configParam := strings.Fields(line)
if len(configParam) == 0 {
continue
}
if len(configParam) != 2 {
return fmt.Errorf("failed to read configuration file. Configuration %s is incorrect. Make sure your configuration file parameters are of the format `key value`", configParam)
}
// get corresponding Blobfuse2 configurations from the config file parameters
err := convertBfConfigParameter(cmd.Flags(), configParam[0], configParam[1])
if err != nil {
return fmt.Errorf("failed to convert configuration parameters [%s]", err.Error())
}
}
}
bfv2ComponentsConfigOptions = append(bfv2ComponentsConfigOptions, "libfuse")
// get corresponding Blobfuse2 configurations from the cli parameters - these supersede the config options
err = convertBfCliParameters(cmd.Flags())
if err != nil {
return fmt.Errorf("failed to convert CLI parameters [%s]", err.Error())
}
// if we have the o being passed then parse it
if cmd.Flags().Lookup("o").Changed {
err := parseFuseConfig(libfuseOptions)
if err != nil {
return err
}
}
if useStream {
bfv2ComponentsConfigOptions = append(bfv2ComponentsConfigOptions, "stream")
}
if useFileCache {
bfv2ComponentsConfigOptions = append(bfv2ComponentsConfigOptions, "file_cache")
}
if useAttrCache {
bfv2ComponentsConfigOptions = append(bfv2ComponentsConfigOptions, "attr_cache")
}
bfv2ComponentsConfigOptions = append(bfv2ComponentsConfigOptions, "azstorage")
// Set the endpoint if not explicitly provided
if bfv2StorageConfigOptions.Endpoint == "" {
accountName := bfv2StorageConfigOptions.AccountName
if accountName == "" {
res, ok := os.LookupEnv(azstorage.EnvAzStorageAccount)
if !ok {
return fmt.Errorf("invalid account name")
} else {
accountName = res
}
}
http := "https"
if bfv2StorageConfigOptions.UseHTTP {
http = "http"
}
accountType := ""
if bfv2StorageConfigOptions.AccountType == "" || bfv2StorageConfigOptions.AccountType == "blob" {
accountType = "blob"
} else if bfv2StorageConfigOptions.AccountType == "adls" {
accountType = "dfs"
} else {
return fmt.Errorf("invalid account type")
}
bfv2StorageConfigOptions.Endpoint = fmt.Sprintf("%s://%s.%s.core.windows.net", http, accountName, accountType)
}
bfv2StorageConfigOptions.VirtualDirectory = true
pConf := PipelineConfig{
bfv2ForegroundOption,
bfv2ReadOnlyOption,
bfv2AllowOtherOption,
bfv2NonEmptyMountOption,
bfv2LoggingConfigOptions,
bfv2FuseConfigOptions,
bfv2StreamConfigOptions,
bfv2FileCacheConfigOptions,
bfv2AttrCacheConfigOptions,
bfv2StorageConfigOptions,
bfv2ComponentsConfigOptions}
data, _ := yaml.Marshal(&pConf)
err2 := os.WriteFile(outputFilePath, data, 0700)
if err2 != nil {
return fmt.Errorf("failed to write file [%s]", err2.Error())
}
if !convertConfigOnly {
buf := new(bytes.Buffer)
rootCmd.SetOut(buf)
rootCmd.SetErr(buf)
if enableGen1 {
rootCmd.SetArgs([]string{"mountgen1", mountPath, fmt.Sprintf("--config-file=%s", outputFilePath), fmt.Sprintf("--required-free-space-mb=%v", reqFreeSpaceMB)})
} else {
rootCmd.SetArgs([]string{"mount", mountPath, fmt.Sprintf("--config-file=%s", outputFilePath), "--disable-version-check=true"})
}
err := rootCmd.Execute()
if err != nil {
return fmt.Errorf("failed to execute command [%s]", err.Error())
}
}
return nil
},
}
// `-o negative_timeout`: files that do not exist
// `-o ro`: read-only mode
// `-o entry_timeout`: timeout in seconds for which name lookups will be cached
// `-o attr_timeout`: The timeout in seconds for which file/directory attributes
// `-o umask`: inverse of default permissions being set, so 0000 is 0777
// `-d` : enable debug logs and foreground on
func parseFuseConfig(config []string) error {
for _, v := range config {
parameter := strings.Split(v, "=")
if len(parameter) > 2 || len(parameter) <= 0 {
return errors.New(common.FuseAllowedFlags)
}
v = strings.TrimSpace(v)
if ignoreFuseOptions(v) {
continue
} else if v == "allow_other" || v == "allow_other=true" {
bfv2AllowOtherOption = true
} else if v == "allow_other=false" {
bfv2AllowOtherOption = false
} else if v == "nonempty" {
bfv2NonEmptyMountOption = true
} else if strings.HasPrefix(v, "attr_timeout=") {
timeout, err := strconv.ParseUint(parameter[1], 10, 32)
if err != nil {
return fmt.Errorf("failed to parse attr_timeout [%s]", err.Error())
}
bfv2FuseConfigOptions.AttributeExpiration = uint32(timeout)
} else if strings.HasPrefix(v, "entry_timeout=") {
timeout, err := strconv.ParseUint(parameter[1], 10, 32)
if err != nil {
return fmt.Errorf("failed to parse entry_timeout [%s]", err.Error())
}
bfv2FuseConfigOptions.EntryExpiration = uint32(timeout)
} else if strings.HasPrefix(v, "negative_timeout=") {
timeout, err := strconv.ParseUint(parameter[1], 10, 32)
if err != nil {
return fmt.Errorf("failed to parse negative_timeout [%s]", err.Error())
}
bfv2FuseConfigOptions.NegativeEntryExpiration = uint32(timeout)
} else if v == "ro" {
bfv2ReadOnlyOption = true
} else if v == "allow_root" {
bfv2FuseConfigOptions.DefaultPermission = 700
} else if strings.HasPrefix(v, "umask=") {
permission, err := strconv.ParseUint(parameter[1], 10, 32)
if err != nil {
return fmt.Errorf("failed to parse umask [%s]", err.Error())
}
perm := ^uint32(permission) & 777
bfv2FuseConfigOptions.DefaultPermission = perm
} else {
return errors.New(common.FuseAllowedFlags)
}
}
return nil
}
// helper method: converts config file options
func convertBfConfigParameter(flags *pflag.FlagSet, configParameterKey string, configParameterValue string) error {
switch configParameterKey {
case "logLevel":
if !flags.Lookup("log-level").Changed {
bfv2LoggingConfigOptions.LogLevel = configParameterValue
}
case "accountName":
bfv2StorageConfigOptions.AccountName = configParameterValue
case "accountKey":
bfv2StorageConfigOptions.AccountKey = configParameterValue
case "accountType":
if !flags.Lookup("use-adls").Changed {
bfv2StorageConfigOptions.AccountType = configParameterValue
}
case "aadEndpoint":
bfv2StorageConfigOptions.ActiveDirectoryEndpoint = configParameterValue
case "authType":
bfv2StorageConfigOptions.AuthMode = strings.ToLower(configParameterValue)
case "blobEndpoint":
bfv2StorageConfigOptions.Endpoint = configParameterValue
case "containerName":
if !flags.Lookup("container-name").Changed {
bfv2StorageConfigOptions.Container = configParameterValue
}
case "httpProxy":
if !flags.Lookup("http-proxy").Changed {
bfv2StorageConfigOptions.HttpProxyAddress = configParameterValue
}
case "identityClientId":
bfv2StorageConfigOptions.ApplicationID = configParameterValue
case "httpsProxy":
bfv2StorageConfigOptions.HttpsProxyAddress = configParameterValue
case "identityObjectId":
bfv2StorageConfigOptions.ObjectID = configParameterValue
case "identityResourceId":
bfv2StorageConfigOptions.ResourceID = configParameterValue
case "sasToken":
bfv2StorageConfigOptions.SaSKey = configParameterValue
case "servicePrincipalClientId":
bfv2StorageConfigOptions.ClientID = configParameterValue
case "servicePrincipalClientSecret":
bfv2StorageConfigOptions.ClientSecret = configParameterValue
case "servicePrincipalTenantId":
bfv2StorageConfigOptions.TenantID = configParameterValue
case "msiEndpoint":
// msiEndpoint is not supported config in V2, this needs to be given as MSI_ENDPOINT env variable
return nil
default:
return fmt.Errorf("failed to parse configuration file. Configuration parameter `%s` is not supported in Blobfuse2", configParameterKey)
}
return nil
}
// helper method: converts cli options - cli options that overlap with config file take precedence
func convertBfCliParameters(flags *pflag.FlagSet) error {
if flags.Lookup("set-content-type").Changed || flags.Lookup("ca-cert-file").Changed || flags.Lookup("basic-remount-check").Changed || flags.Lookup(
"background-download").Changed || flags.Lookup("cache-poll-timeout-msec").Changed || flags.Lookup("upload-modified-only").Changed || flags.Lookup("debug-libcurl").Changed {
logWriter, _ := syslog.New(syslog.LOG_WARNING, "")
_ = logWriter.Warning("one or more unsupported v1 parameters [set-content-type, ca-cert-file, basic-remount-check, background-download, cache-poll-timeout-msec, upload-modified-only, debug-libcurl] have been passed, ignoring and proceeding to mount")
}
bfv2LoggingConfigOptions.Type = "syslog"
if flags.Lookup("log-level").Changed {
bfv2LoggingConfigOptions.LogLevel = bfConfCliOptions.logLevel
}
if flags.Lookup("streaming").Changed {
if bfConfCliOptions.useStreaming {
useStream = true
useFileCache = false
if flags.Lookup("block-size-mb").Changed {
bfv2StreamConfigOptions.BlockSize = bfConfCliOptions.blockSize
}
if flags.Lookup("max-blocks-per-file").Changed {
bfv2StreamConfigOptions.BufferSize = bfConfCliOptions.blockSize * uint64(bfConfCliOptions.maxBlocksPerFile)
}
if flags.Lookup("stream-cache-mb").Changed {
bfv2StreamConfigOptions.CachedObjLimit = bfConfCliOptions.streamCacheSize / bfv2StreamConfigOptions.BufferSize
if bfv2StreamConfigOptions.CachedObjLimit == 0 {
bfv2StreamConfigOptions.CachedObjLimit = 1
}
}
} else {
useStream = false
useFileCache = true
}
}
if flags.Lookup("use-attr-cache").Changed {
useAttrCache = true
if bfConfCliOptions.useAttrCache {
if flags.Lookup("cache-on-list").Changed {
if bfConfCliOptions.cacheOnList {
bfv2AttrCacheConfigOptions.NoCacheOnList = !bfConfCliOptions.cacheOnList
}
}
if flags.Lookup("no-symlinks").Changed {
if bfConfCliOptions.noSymlinks {
bfv2AttrCacheConfigOptions.NoSymlinks = bfConfCliOptions.noSymlinks
}
}
}
}
if flags.Lookup("tmp-path").Changed {
bfv2FileCacheConfigOptions.TmpPath = bfConfCliOptions.tmpPath
}
if flags.Lookup("cache-size-mb").Changed {
bfv2FileCacheConfigOptions.MaxSizeMB = bfConfCliOptions.cacheSize
}
if flags.Lookup("file-cache-timeout-in-seconds").Changed {
bfv2FileCacheConfigOptions.Timeout = bfConfCliOptions.fileCacheTimeout
}
if flags.Lookup("max-eviction").Changed {
bfv2FileCacheConfigOptions.MaxEviction = bfConfCliOptions.maxEviciton
}
if flags.Lookup("high-disk-threshold").Changed {
bfv2FileCacheConfigOptions.HighThreshold = bfConfCliOptions.highDiskThreshold
}
if flags.Lookup("low-disk-threshold").Changed {
bfv2FileCacheConfigOptions.LowThreshold = bfConfCliOptions.lowDiskThreshold
}
if flags.Lookup("empty-dir-check").Changed {
bfv2FileCacheConfigOptions.AllowNonEmpty = !bfConfCliOptions.emptyDirCheck
}
if flags.Lookup("use-adls").Changed {
if bfConfCliOptions.useAdls {
bfv2StorageConfigOptions.AccountType = "adls"
} else {
bfv2StorageConfigOptions.AccountType = "block"
}
}
if flags.Lookup("use-https").Changed {
bfv2StorageConfigOptions.UseHTTP = !bfConfCliOptions.useHttps
}
if flags.Lookup("container-name").Changed {
bfv2StorageConfigOptions.Container = bfConfCliOptions.containerName
}
if flags.Lookup("max-concurrency").Changed {
bfv2StorageConfigOptions.MaxConcurrency = bfConfCliOptions.maxConcurrency
}
if flags.Lookup("cancel-list-on-mount-seconds").Changed {
bfv2StorageConfigOptions.CancelListForSeconds = bfConfCliOptions.cancelListOnMount
}
if flags.Lookup("max-retry").Changed {
bfv2StorageConfigOptions.MaxRetries = bfConfCliOptions.maxRetry
}
if flags.Lookup("max-retry-interval-in-seconds").Changed {
bfv2StorageConfigOptions.MaxTimeout = bfConfCliOptions.maxRetryInterval
}
if flags.Lookup("retry-delay-factor").Changed {
bfv2StorageConfigOptions.BackoffTime = bfConfCliOptions.retryDelayFactor
}
if flags.Lookup("http-proxy").Changed {
bfv2StorageConfigOptions.HttpProxyAddress = bfConfCliOptions.httpProxy
}
if flags.Lookup("https-proxy").Changed {
bfv2StorageConfigOptions.HttpsProxyAddress = bfConfCliOptions.httpsProxy
}
if flags.Lookup("d").Changed {
bfv2FuseConfigOptions.EnableFuseTrace = bfConfCliOptions.fuseLogging
bfv2ForegroundOption = bfConfCliOptions.fuseLogging
}
if flags.Lookup("ignore-open-flags").Changed {
bfv2FuseConfigOptions.IgnoreOpenFlags = bfConfCliOptions.ignoreOpenFlags
}
return nil
}
func init() {
rootCmd.AddCommand(generateConfigCmd)
generateConfigCmd.Flags().StringVar(&outputFilePath, "output-file", "config.yaml", "Output Blobfuse configuration file.")
generateConfigCmd.Flags().StringVar(&bfConfCliOptions.tmpPath, "tmp-path", "", "Tmp location for the file cache.")
generateConfigCmd.Flags().StringVar(&bfConfCliOptions.configFile, "config-file", "", "Input Blobfuse configuration file.")
generateConfigCmd.Flags().BoolVar(&bfConfCliOptions.useHttps, "use-https", false, "Enables HTTPS communication with Blob storage.")
generateConfigCmd.Flags().Uint32Var(&bfConfCliOptions.fileCacheTimeout, "file-cache-timeout-in-seconds", 0, "During this time, blobfuse will not check whether the file is up to date or not.")
generateConfigCmd.Flags().StringVar(&bfConfCliOptions.containerName, "container-name", "", "Required if no configuration file is specified.")
generateConfigCmd.Flags().StringVar(&bfConfCliOptions.logLevel, "log-level", "LOG_WARNING", "Logging level.")
generateConfigCmd.Flags().BoolVar(&bfConfCliOptions.useAttrCache, "use-attr-cache", false, "Enable attribute cache.")
generateConfigCmd.Flags().BoolVar(&bfConfCliOptions.useAdls, "use-adls", false, "Enables blobfuse to access Azure DataLake storage account.")
generateConfigCmd.Flags().BoolVar(&bfConfCliOptions.noSymlinks, "no-symlinks", false, "Disables symlink support.")
generateConfigCmd.Flags().BoolVar(&bfConfCliOptions.cacheOnList, "cache-on-list", true, "Cache attributes on listing.")
generateConfigCmd.Flags().Uint16Var(&bfConfCliOptions.maxConcurrency, "max-concurrency", 0, "Option to override default number of concurrent storage connections")
generateConfigCmd.Flags().Float64Var(&bfConfCliOptions.cacheSize, "cache-size-mb", 0, "File cache size.")
generateConfigCmd.Flags().BoolVar(&bfConfCliOptions.emptyDirCheck, "empty-dir-check", false, "Disallows remounting using a non-empty tmp-path.")
generateConfigCmd.Flags().Uint16Var(&bfConfCliOptions.cancelListOnMount, "cancel-list-on-mount-seconds", 0, "A list call to the container is by default issued on mount.")
generateConfigCmd.Flags().Uint32Var(&bfConfCliOptions.highDiskThreshold, "high-disk-threshold", 0, "High disk threshold percentage.")
generateConfigCmd.Flags().Uint32Var(&bfConfCliOptions.lowDiskThreshold, "low-disk-threshold", 0, "Low disk threshold percentage.")
generateConfigCmd.Flags().Uint32Var(&bfConfCliOptions.maxEviciton, "max-eviction", 0, "Number of files to be evicted from cache at once.")
generateConfigCmd.Flags().StringVar(&bfConfCliOptions.httpsProxy, "https-proxy", "", "HTTPS Proxy address.")
generateConfigCmd.Flags().StringVar(&bfConfCliOptions.httpProxy, "http-proxy", "", "HTTP Proxy address.")
generateConfigCmd.Flags().Int32Var(&bfConfCliOptions.maxRetry, "max-retry", 0, "Maximum retry count if the failure codes are retryable.")
generateConfigCmd.Flags().Int32Var(&bfConfCliOptions.maxRetryInterval, "max-retry-interval-in-seconds", 0, "Maximum number of seconds between 2 retries.")
generateConfigCmd.Flags().Int32Var(&bfConfCliOptions.retryDelayFactor, "retry-delay-factor", 0, "Retry delay between two tries")
//invalidate-on-sync is always on - accept it as an arg and just ignore it
generateConfigCmd.Flags().Bool("invalidate-on-sync", true, "Invalidate file/dir on sync/fsync")
//pre-mount-validate is always on - accept it as an arg and just ignore it
generateConfigCmd.Flags().Bool("pre-mount-validate", true, "Validate blobfuse2 is mounted")
generateConfigCmd.Flags().BoolVar(&bfConfCliOptions.useStreaming, "streaming", false, "Enable Streaming.")
generateConfigCmd.Flags().Uint64Var(&bfConfCliOptions.streamCacheSize, "stream-cache-mb", 0, "Limit total amount of data being cached in memory to conserve memory footprint of blobfuse.")
generateConfigCmd.Flags().IntVar(&bfConfCliOptions.maxBlocksPerFile, "max-blocks-per-file", 0, "Maximum number of blocks to be cached in memory for streaming.")
generateConfigCmd.Flags().Uint64Var(&bfConfCliOptions.blockSize, "block-size-mb", 0, "Size (in MB) of a block to be downloaded during streaming.")
generateConfigCmd.Flags().StringSliceVarP(&libfuseOptions, "o", "o", []string{}, "FUSE options.")
generateConfigCmd.Flags().BoolVarP(&bfConfCliOptions.fuseLogging, "d", "d", false, "Mount with foreground and FUSE logs on.")
generateConfigCmd.Flags().BoolVar(&convertConfigOnly, "convert-config-only", false, "Don't mount - only convert v1 configuration to v2.")
generateConfigCmd.Flags().BoolVar(&bfConfCliOptions.ignoreOpenFlags, "ignore-open-flags", false, "Flag to ignore open flags unsupported by blobfuse.")
// options that are not available in V2:
generateConfigCmd.Flags().Bool("set-content-type", false, "Turns on automatic 'content-type' property based on the file extension.")
generateConfigCmd.Flags().String("ca-cert-file", "", "Specifies the proxy pem certificate path if its not in the default path.")
generateConfigCmd.Flags().Bool("basic-remount-check", false, "Check for an already mounted status using /etc/mtab.")
generateConfigCmd.Flags().Bool("background-download", false, "File download to run in the background on open call.")
generateConfigCmd.Flags().Uint64("cache-poll-timeout-msec", 0, "Time in milliseconds in order to poll for possible expired files awaiting cache eviction.")
generateConfigCmd.Flags().Bool("upload-modified-only", false, "Flag to turn off unnecessary uploads to storage.")
generateConfigCmd.Flags().Bool("debug-libcurl", false, "Flag to allow users to debug libcurl calls.")
// flags for gen1 mount
generateConfigCmd.Flags().BoolVar(&enableGen1, "enable-gen1", false, "To enable Gen1 mount")
generateConfigCmd.Flags().IntVar(&reqFreeSpaceMB, "required-free-space-mb", 0, "Required free space in MB")
}