|
|
@@ -1,25 +1,17 @@
|
|
|
package env
|
|
|
|
|
|
import (
|
|
|
- "github.com/opencost/opencost/core/pkg/env"
|
|
|
-)
|
|
|
+ "time"
|
|
|
|
|
|
-// FilePaths
|
|
|
-const (
|
|
|
- ClusterInfoFile = "cluster-info.json"
|
|
|
- ClusterCacheFile
|
|
|
- GCPAuthSecretFile = "key.json"
|
|
|
- MetricConfigFile = "metrics.json"
|
|
|
+ "github.com/opencost/opencost/core/pkg/env"
|
|
|
+ "github.com/opencost/opencost/core/pkg/log"
|
|
|
+ "github.com/opencost/opencost/core/pkg/util/timeutil"
|
|
|
)
|
|
|
|
|
|
-// Env Variables
|
|
|
const (
|
|
|
- // Open configs
|
|
|
-
|
|
|
- // We assume that Kubernetes is enabled if there is a KUBERNETES_PORT environment variable present
|
|
|
- KubernetesEnabledEnvVar = "KUBERNETES_PORT"
|
|
|
+ APIPortEnvVar = "API_PORT"
|
|
|
+ NetworkCostsPortEnvVar = "NETWORK_COSTS_PORT"
|
|
|
|
|
|
- // Cloud Provider
|
|
|
AWSAccessKeyIDEnvVar = "AWS_ACCESS_KEY_ID"
|
|
|
AWSAccessKeySecretEnvVar = "AWS_SECRET_ACCESS_KEY"
|
|
|
AWSClusterIDEnvVar = "AWS_CLUSTER_ID"
|
|
|
@@ -28,23 +20,26 @@ const (
|
|
|
AlibabaAccessKeyIDEnvVar = "ALIBABA_ACCESS_KEY_ID"
|
|
|
AlibabaAccessKeySecretEnvVar = "ALIBABA_SECRET_ACCESS_KEY"
|
|
|
|
|
|
- AzureOfferIDEnvVar = "AZURE_OFFER_ID"
|
|
|
- AzureBillingAccountEnvVar = "AZURE_BILLING_ACCOUNT"
|
|
|
-
|
|
|
- OCIPricingURL = "OCI_PRICING_URL"
|
|
|
-
|
|
|
- ClusterProfileEnvVar = "CLUSTER_PROFILE"
|
|
|
- RemoteEnabledEnvVar = "REMOTE_WRITE_ENABLED"
|
|
|
- RemotePWEnvVar = "REMOTE_WRITE_PASSWORD"
|
|
|
- SQLAddressEnvVar = "SQL_ADDRESS"
|
|
|
- UseCSVProviderEnvVar = "USE_CSV_PROVIDER"
|
|
|
- UseCustomProviderEnvVar = "USE_CUSTOM_PROVIDER"
|
|
|
- CSVRegionEnvVar = "CSV_REGION"
|
|
|
- CSVEndpointEnvVar = "CSV_ENDPOINT"
|
|
|
- CSVPathEnvVar = "CSV_PATH"
|
|
|
-
|
|
|
+ AzureOfferIDEnvVar = "AZURE_OFFER_ID"
|
|
|
+ AzureBillingAccountEnvVar = "AZURE_BILLING_ACCOUNT"
|
|
|
+ AzureDownloadBillingDataToDiskEnvVar = "AZURE_DOWNLOAD_BILLING_DATA_TO_DISK"
|
|
|
+
|
|
|
+ ReleaseNameEnvVar = "RELEASE_NAME"
|
|
|
+ PodNameEnvVar = "POD_NAME"
|
|
|
+ ClusterIDEnvVar = "CLUSTER_ID"
|
|
|
+ ClusterProfileEnvVar = "CLUSTER_PROFILE"
|
|
|
+ RemoteEnabledEnvVar = "REMOTE_WRITE_ENABLED"
|
|
|
+ RemotePWEnvVar = "REMOTE_WRITE_PASSWORD"
|
|
|
+ SQLAddressEnvVar = "SQL_ADDRESS"
|
|
|
+ UseCSVProviderEnvVar = "USE_CSV_PROVIDER"
|
|
|
+ UseCustomProviderEnvVar = "USE_CUSTOM_PROVIDER"
|
|
|
+ CSVRegionEnvVar = "CSV_REGION"
|
|
|
+ CSVEndpointEnvVar = "CSV_ENDPOINT"
|
|
|
+ CSVPathEnvVar = "CSV_PATH"
|
|
|
+ ConfigPathEnvVar = "CONFIG_PATH"
|
|
|
CloudProviderAPIKeyEnvVar = "CLOUD_PROVIDER_API_KEY"
|
|
|
CollectorDataSourceEnabledEnvVar = "COLLECTOR_DATA_SOURCE_ENABLED"
|
|
|
+ PVMountPath = "PV_MOUNT_PATH"
|
|
|
|
|
|
EmitPodAnnotationsMetricEnvVar = "EMIT_POD_ANNOTATIONS_METRIC"
|
|
|
EmitNamespaceAnnotationsMetricEnvVar = "EMIT_NAMESPACE_ANNOTATIONS_METRIC"
|
|
|
@@ -53,18 +48,29 @@ const (
|
|
|
EmitKsmV1MetricsEnvVar = "EMIT_KSM_V1_METRICS"
|
|
|
EmitKsmV1MetricsOnly = "EMIT_KSM_V1_METRICS_ONLY"
|
|
|
|
|
|
+ PProfEnabledEnvVar = "PPROF_ENABLED"
|
|
|
+
|
|
|
LogCollectionEnabledEnvVar = "LOG_COLLECTION_ENABLED"
|
|
|
ProductAnalyticsEnabledEnvVar = "PRODUCT_ANALYTICS_ENABLED"
|
|
|
ErrorReportingEnabledEnvVar = "ERROR_REPORTING_ENABLED"
|
|
|
ValuesReportingEnabledEnvVar = "VALUES_REPORTING_ENABLED"
|
|
|
|
|
|
+ KubeRbacProxyEnabled = "KUBE_RBAC_PROXY_ENABLED"
|
|
|
+
|
|
|
+ KubeConfigPathEnvVar = "KUBECONFIG_PATH"
|
|
|
+
|
|
|
+ UTCOffsetEnvVar = "UTC_OFFSET"
|
|
|
+
|
|
|
PricingConfigmapName = "PRICING_CONFIGMAP_NAME"
|
|
|
MetricsConfigmapName = "METRICS_CONFIGMAP_NAME"
|
|
|
|
|
|
- ClusterInfoFileEnabledEnvVar = "CLUSTER_INFO_FILE_ENABLED"
|
|
|
+ ClusterInfoFileEnabledEnvVar = "CLUSTER_INFO_FILE_ENABLED"
|
|
|
+ ClusterCacheFileEnabledEnvVar = "CLUSTER_CACHE_FILE_ENABLED"
|
|
|
|
|
|
IngestPodUIDEnvVar = "INGEST_POD_UID"
|
|
|
|
|
|
+ ETLReadOnlyMode = "ETL_READ_ONLY"
|
|
|
+
|
|
|
AllocationNodeLabelsEnabled = "ALLOCATION_NODE_LABELS_ENABLED"
|
|
|
|
|
|
AssetIncludeLocalDiskCostEnvVar = "ASSET_INCLUDE_LOCAL_DISK_COST"
|
|
|
@@ -76,20 +82,55 @@ const (
|
|
|
ExportCSVLabelsAll = "EXPORT_CSV_LABELS_ALL"
|
|
|
ExportCSVMaxDays = "EXPORT_CSV_MAX_DAYS"
|
|
|
|
|
|
+ ExportBucketConfigFileEnvVar = "EXPORT_BUCKET_CONFIG_FILE"
|
|
|
+
|
|
|
DataRetentionDailyResolutionDaysEnvVar = "DATA_RETENTION_DAILY_RESOLUTION_DAYS"
|
|
|
DataRetentionHourlyResolutionHoursEnvVar = "DATA_RETENTION_HOURLY_RESOLUTION_HOURS"
|
|
|
|
|
|
+ // We assume that Kubernetes is enabled if there is a KUBERNETES_PORT environment variable present
|
|
|
+ KubernetesEnabledEnvVar = "KUBERNETES_PORT"
|
|
|
+ CloudCostEnabledEnvVar = "CLOUD_COST_ENABLED"
|
|
|
+ CloudCostConfigPath = "CLOUD_COST_CONFIG_PATH"
|
|
|
+ CloudCostMonthToDateIntervalVar = "CLOUD_COST_MONTH_TO_DATE_INTERVAL"
|
|
|
+ CloudCostRefreshRateHoursEnvVar = "CLOUD_COST_REFRESH_RATE_HOURS"
|
|
|
+ CloudCostQueryWindowDaysEnvVar = "CLOUD_COST_QUERY_WINDOW_DAYS"
|
|
|
+ CloudCostRunWindowDaysEnvVar = "CLOUD_COST_RUN_WINDOW_DAYS"
|
|
|
+
|
|
|
+ CustomCostEnabledEnvVar = "CUSTOM_COST_ENABLED"
|
|
|
+ CustomCostQueryWindowDaysEnvVar = "CUSTOM_COST_QUERY_WINDOW_DAYS"
|
|
|
+ CustomCostRefreshRateHoursEnvVar = "CUSTOM_COST_REFRESH_RATE_HOURS"
|
|
|
+
|
|
|
+ PluginConfigDirEnvVar = "PLUGIN_CONFIG_DIR"
|
|
|
+ PluginExecutableDirEnvVar = "PLUGIN_EXECUTABLE_DIR"
|
|
|
+
|
|
|
+ OCIPricingURL = "OCI_PRICING_URL"
|
|
|
+
|
|
|
CarbonEstimatesEnabledEnvVar = "CARBON_ESTIMATES_ENABLED"
|
|
|
|
|
|
- KubernetesResourceAccessEnvVar = "KUBERNETES_RESOURCE_ACCESS"
|
|
|
- UseCacheV1 = "USE_CACHE_V1"
|
|
|
+ UseCacheV1 = "USE_CACHE_V1"
|
|
|
+
|
|
|
+ InstallNamespaceEnvVar = "INSTALL_NAMESPACE"
|
|
|
+ ConfigBucketEnvVar = "CONFIG_BUCKET"
|
|
|
+
|
|
|
+ // Node Stats Client Configuration
|
|
|
+ NodeStatsForceKubeProxyEnvVar = "NODESTATS_FORCE_KUBE_PROXY"
|
|
|
+ NodeStatsLocalProxyEnvVar = "NODESTATS_LOCAL_PROXY"
|
|
|
+ NodeStatsInsecureEnvVar = "NODESTATS_INSECURE"
|
|
|
+ NodeStatsCertFileEnvVar = "NODESTATS_CERT_FILE"
|
|
|
+ NodeStatsKeyFileEnvVar = "NODESTATS_KEY_FILE"
|
|
|
+
|
|
|
+ // Deprecated
|
|
|
+ KubecostNamespaceEnvVar = "KUBECOST_NAMESPACE"
|
|
|
+ KubecostConfigBucketEnvVar = "KUBECOST_CONFIG_BUCKET"
|
|
|
|
|
|
// Cloud provider override
|
|
|
CloudProviderVar = "CLOUD_PROVIDER"
|
|
|
)
|
|
|
|
|
|
-func GetGCPAuthSecretFilePath() string {
|
|
|
- return env.GetPathFromConfig(GCPAuthSecretFile)
|
|
|
+const DefaultConfigMountPath = "/var/configs"
|
|
|
+
|
|
|
+func IsETLReadOnlyMode() bool {
|
|
|
+ return env.GetBool(ETLReadOnlyMode, false)
|
|
|
}
|
|
|
|
|
|
func GetExportCSVFile() string {
|
|
|
@@ -104,22 +145,36 @@ func GetExportCSVLabelsList() []string {
|
|
|
return env.GetList(ExportCSVLabelsList, ",")
|
|
|
}
|
|
|
|
|
|
+func IsPProfEnabled() bool {
|
|
|
+ return env.GetBool(PProfEnabledEnvVar, false)
|
|
|
+}
|
|
|
+
|
|
|
func GetExportCSVMaxDays() int {
|
|
|
return env.GetInt(ExportCSVMaxDays, 90)
|
|
|
}
|
|
|
|
|
|
+// GetAPIPort returns the environment variable value for APIPortEnvVar which
|
|
|
+// is the port number the API is available on.
|
|
|
+func GetAPIPort() int {
|
|
|
+ return env.GetInt(APIPortEnvVar, 9003)
|
|
|
+}
|
|
|
+
|
|
|
+// GetConfigBucketFile returns a file location for a mounted bucket configuration which is used to store
|
|
|
+// a subset of configurations that require sharing via remote storage.
|
|
|
+func GetConfigBucketFile() string {
|
|
|
+ return env.Get(ConfigBucketEnvVar, env.Get(KubecostConfigBucketEnvVar, ""))
|
|
|
+}
|
|
|
+
|
|
|
// IsClusterInfoFileEnabled returns true if the cluster info is read from a file or pulled from the local
|
|
|
// cloud provider and kubernetes.
|
|
|
func IsClusterInfoFileEnabled() bool {
|
|
|
return env.GetBool(ClusterInfoFileEnabledEnvVar, false)
|
|
|
}
|
|
|
|
|
|
-func GetClusterInfoFilePath() string {
|
|
|
- return env.GetPathFromConfig(ClusterInfoFile)
|
|
|
-}
|
|
|
-
|
|
|
-func GetClusterCacheFilePath() string {
|
|
|
- return env.GetPathFromConfig(ClusterCacheFile)
|
|
|
+// IsClusterCacheFileEnabled returns true if the kubernetes cluster data is read from a file or pulled from the local
|
|
|
+// kubernetes API.
|
|
|
+func IsClusterCacheFileEnabled() bool {
|
|
|
+ return env.GetBool(ClusterCacheFileEnabledEnvVar, false)
|
|
|
}
|
|
|
|
|
|
func GetPricingConfigmapName() string {
|
|
|
@@ -219,12 +274,34 @@ func IsAzureDownloadBillingDataToDisk() bool {
|
|
|
return env.GetBool(AzureDownloadBillingDataToDiskEnvVar, true)
|
|
|
}
|
|
|
|
|
|
+// GetInstallNamespace returns the environment variable value that is set for the kubernetes namespace
|
|
|
+// this service is installed in.
|
|
|
+func GetInstallNamespace() string {
|
|
|
+ return env.Get(InstallNamespaceEnvVar, env.Get(KubecostNamespaceEnvVar, "opencost"))
|
|
|
+}
|
|
|
+
|
|
|
+// GetPodName returns the name of the current running pod. If this environment variable is not set,
|
|
|
+// empty string is returned.
|
|
|
+func GetPodName() string {
|
|
|
+ return env.Get(PodNameEnvVar, "")
|
|
|
+}
|
|
|
+
|
|
|
// GetClusterProfile returns the environment variable value for ClusterProfileEnvVar which
|
|
|
// represents the cluster profile configured for
|
|
|
func GetClusterProfile() string {
|
|
|
return env.Get(ClusterProfileEnvVar, "development")
|
|
|
}
|
|
|
|
|
|
+// GetClusterID returns the environment variable value for ClusterIDEnvVar which represents the
|
|
|
+// configurable identifier used for multi-cluster metric emission.
|
|
|
+func GetClusterID() string {
|
|
|
+ return env.Get(ClusterIDEnvVar, "")
|
|
|
+}
|
|
|
+
|
|
|
+func IsKubeRbacProxyEnabled() bool {
|
|
|
+ return env.GetBool(KubeRbacProxyEnabled, false)
|
|
|
+}
|
|
|
+
|
|
|
// IsRemoteEnabled returns the environment variable value for RemoteEnabledEnvVar which represents whether
|
|
|
// or not remote write is enabled for prometheus for use with SQL backed persistent storage.
|
|
|
func IsRemoteEnabled() bool {
|
|
|
@@ -273,12 +350,28 @@ func GetCSVPath() string {
|
|
|
return env.Get(CSVPathEnvVar, "")
|
|
|
}
|
|
|
|
|
|
+// GetCostAnalyzerVolumeMountPath is an alias of GetConfigPath, which returns the mount path for the
|
|
|
+// Cost Analyzer volume, which stores configs, persistent data, etc.
|
|
|
+func GetCostAnalyzerVolumeMountPath() string {
|
|
|
+ return GetConfigPathWithDefault(DefaultConfigMountPath)
|
|
|
+}
|
|
|
+
|
|
|
+// GetConfigPath returns the environment variable value for ConfigPathEnvVar which represents the cost
|
|
|
+// model configuration path
|
|
|
+func GetConfigPathWithDefault(defaultValue string) string {
|
|
|
+ return env.Get(ConfigPathEnvVar, defaultValue)
|
|
|
+}
|
|
|
+
|
|
|
// GetCloudProviderAPI returns the environment variable value for CloudProviderAPIEnvVar which represents
|
|
|
// the API key provided for the cloud provider.
|
|
|
func GetCloudProviderAPIKey() string {
|
|
|
return env.Get(CloudProviderAPIKeyEnvVar, "")
|
|
|
}
|
|
|
|
|
|
+func GetPVMountPath() string {
|
|
|
+ return env.Get(PVMountPath, "")
|
|
|
+}
|
|
|
+
|
|
|
// IsCollectorDataSourceEnabeled returns the environment variable which enables a source.OpencostDatasource which does not use uses Prometheus
|
|
|
func IsCollectorDataSourceEnabled() bool {
|
|
|
return env.GetBool(CollectorDataSourceEnabledEnvVar, false)
|
|
|
@@ -305,6 +398,26 @@ func IsValuesReportingEnabled() bool {
|
|
|
return env.GetBool(ValuesReportingEnabledEnvVar, true)
|
|
|
}
|
|
|
|
|
|
+// GetKubeConfigPath returns the environment variable value for KubeConfigPathEnvVar
|
|
|
+func GetKubeConfigPath() string {
|
|
|
+ return env.Get(KubeConfigPathEnvVar, "")
|
|
|
+}
|
|
|
+
|
|
|
+// GetUTCOffset returns the environment variable value for UTCOffset
|
|
|
+func GetUTCOffset() string {
|
|
|
+ return env.Get(UTCOffsetEnvVar, "")
|
|
|
+}
|
|
|
+
|
|
|
+// GetParsedUTCOffset returns the duration of the configured UTC offset
|
|
|
+func GetParsedUTCOffset() time.Duration {
|
|
|
+ offset, err := timeutil.ParseUTCOffset(GetUTCOffset())
|
|
|
+ if err != nil {
|
|
|
+ log.Warnf("Failed to parse UTC offset: %s", err)
|
|
|
+ return time.Duration(0)
|
|
|
+ }
|
|
|
+ return offset
|
|
|
+}
|
|
|
+
|
|
|
// IsIngestingPodUID returns the env variable from ingestPodUID, which alters the
|
|
|
// contents of podKeys in Allocation
|
|
|
func IsIngestingPodUID() bool {
|
|
|
@@ -341,16 +454,65 @@ func IsKubernetesEnabled() bool {
|
|
|
return env.Get(KubernetesEnabledEnvVar, "") != ""
|
|
|
}
|
|
|
|
|
|
+func IsCloudCostEnabled() bool {
|
|
|
+ return env.GetBool(CloudCostEnabledEnvVar, false)
|
|
|
+}
|
|
|
+
|
|
|
+func IsCustomCostEnabled() bool {
|
|
|
+ return env.GetBool(CustomCostEnabledEnvVar, false)
|
|
|
+}
|
|
|
+
|
|
|
+func GetCloudCostConfigPath() string {
|
|
|
+ return env.Get(CloudCostConfigPath, "cloud-integration.json")
|
|
|
+}
|
|
|
+
|
|
|
+func GetCloudCostMonthToDateInterval() int {
|
|
|
+ return env.GetInt(CloudCostMonthToDateIntervalVar, 6)
|
|
|
+}
|
|
|
+
|
|
|
+func GetCloudCostRefreshRateHours() int64 {
|
|
|
+ return env.GetInt64(CloudCostRefreshRateHoursEnvVar, 6)
|
|
|
+}
|
|
|
+
|
|
|
+func GetCloudCostQueryWindowDays() int64 {
|
|
|
+ return env.GetInt64(CloudCostQueryWindowDaysEnvVar, 7)
|
|
|
+}
|
|
|
+
|
|
|
+func GetCustomCostQueryWindowHours() int64 {
|
|
|
+ return env.GetInt64(CustomCostQueryWindowDaysEnvVar, 1)
|
|
|
+}
|
|
|
+
|
|
|
+func GetCustomCostQueryWindowDays() int64 {
|
|
|
+ return env.GetInt64(CustomCostQueryWindowDaysEnvVar, 7)
|
|
|
+}
|
|
|
+
|
|
|
+func GetCloudCostRunWindowDays() int64 {
|
|
|
+ return env.GetInt64(CloudCostRunWindowDaysEnvVar, 3)
|
|
|
+}
|
|
|
+
|
|
|
func GetOCIPricingURL() string {
|
|
|
return env.Get(OCIPricingURL, "https://apexapps.oracle.com/pls/apex/cetools/api/v1/products")
|
|
|
}
|
|
|
|
|
|
+func GetPluginConfigDir() string {
|
|
|
+ return env.Get(PluginConfigDirEnvVar, "/opt/opencost/plugin/config")
|
|
|
+}
|
|
|
+
|
|
|
+func GetPluginExecutableDir() string {
|
|
|
+ return env.Get(PluginExecutableDirEnvVar, "/opt/opencost/plugin/bin")
|
|
|
+}
|
|
|
+
|
|
|
+func GetCustomCostRefreshRateHours() string {
|
|
|
+ return env.Get(CustomCostRefreshRateHoursEnvVar, "12h")
|
|
|
+}
|
|
|
+
|
|
|
func IsCarbonEstimatesEnabled() bool {
|
|
|
return env.GetBool(CarbonEstimatesEnabledEnvVar, false)
|
|
|
}
|
|
|
|
|
|
-// HasKubernetesResourceAccess can be set to false if Opencost is run without access to the kubernetes resources
|
|
|
-func HasKubernetesResourceAccess() bool { return env.GetBool(KubernetesResourceAccessEnvVar, true) }
|
|
|
+func GetExportBucketConfigFile() string {
|
|
|
+ return env.Get(ExportBucketConfigFileEnvVar, "")
|
|
|
+}
|
|
|
|
|
|
// GetUseCacheV1 is a temporary flag to allow users to opt-in to using the old cache
|
|
|
// Mainly for comparison purposes
|
|
|
@@ -358,11 +520,42 @@ func GetUseCacheV1() bool {
|
|
|
return env.GetBool(UseCacheV1, false)
|
|
|
}
|
|
|
|
|
|
+func GetReleaseName() string {
|
|
|
+ return env.Get(ReleaseNameEnvVar, "kubecost")
|
|
|
+}
|
|
|
+
|
|
|
+func GetNetworkCostsPort() int {
|
|
|
+ return env.GetInt(NetworkCostsPortEnvVar, 3001)
|
|
|
+}
|
|
|
+
|
|
|
+// IsNodeStatsForceKubeProxy returns true if the node stats client should force the kube proxy direct end
|
|
|
+// point formatting
|
|
|
+func IsNodeStatsForceKubeProxy() bool {
|
|
|
+ return env.GetBool(NodeStatsForceKubeProxyEnvVar, false)
|
|
|
+}
|
|
|
+
|
|
|
+// GetNodeStatsLocalProxy returns the fully qualified local proxy endpoint for the node stats client IFF the proxyAPI
|
|
|
+// is selected.
|
|
|
+func GetNodeStatsLocalProxy() string {
|
|
|
+ return env.Get(NodeStatsLocalProxyEnvVar, "")
|
|
|
+}
|
|
|
+
|
|
|
+// IsNodeStatsInsecure returns true if the node stats client should skip TLS verification
|
|
|
+func IsNodeStatsInsecure() bool {
|
|
|
+ return env.GetBool(NodeStatsInsecureEnvVar, false)
|
|
|
+}
|
|
|
+
|
|
|
+// GetNodeStatsCertFile returns the path of the cert file
|
|
|
+func GetNodeStatsCertFile() string {
|
|
|
+ return env.Get(NodeStatsCertFileEnvVar, "")
|
|
|
+}
|
|
|
+
|
|
|
+// GetNodeStatsKeyFile returns the path of the key file
|
|
|
+func GetNodeStatsKeyFile() string {
|
|
|
+ return env.Get(NodeStatsKeyFileEnvVar, "")
|
|
|
+}
|
|
|
+
|
|
|
// GetCloudProvider returns the explicitly set cloud provider from environment variable
|
|
|
func GetCloudProvider() string {
|
|
|
return env.Get(CloudProviderVar, "")
|
|
|
}
|
|
|
-
|
|
|
-func GetMetricConfigFile() string {
|
|
|
- return env.GetPathFromConfig(MetricConfigFile)
|
|
|
-}
|