From fad8dcac742430395ca9429edac2a7b433dc7d47 Mon Sep 17 00:00:00 2001 From: Mikhail Shagvaliev Date: Fri, 21 Feb 2025 14:26:53 +0100 Subject: [PATCH] :bug: QD-10955 Add QODANA_TOKEN to docker env, QODANA_LICENSE_ONLY_TOKEN accessed only by os.Getenv --- cloud/license.go | 6 ++-- cmd/cmd_test.go | 4 +-- cmd/init.go | 3 +- cmd/pull.go | 1 - cmd/scan.go | 1 - cmd/send.go | 3 +- cmd/show.go | 1 - core/container.go | 12 +++++++- core/core_test.go | 26 ++++++++--------- core/corescan/context.go | 12 +++----- core/corescan/create_context.go | 3 +- core/startup/prepare.go | 23 +++++++-------- platform/commoncontext/compute.go | 22 +++++++------- platform/commoncontext/context.go | 34 +++++++++++----------- platform/env_provider.go | 43 ++++++++++++++++++++++++++++ platform/files.go | 23 --------------- platform/options_test.go | 3 -- platform/qdenv/qdenv.go | 1 + platform/run.go | 3 +- platform/tokenloader/token_loader.go | 19 +++--------- 20 files changed, 120 insertions(+), 123 deletions(-) create mode 100644 platform/env_provider.go diff --git a/cloud/license.go b/cloud/license.go index bfd26d86..83fea669 100644 --- a/cloud/license.go +++ b/cloud/license.go @@ -204,16 +204,16 @@ func GetEnvWithDefaultInt(env string, defaultValue int) int { return result } -func SetupLicenseToken(token string) { +func SetupLicenseToken(cloudUploadToken string) { licenseOnlyToken := os.Getenv(qdenv.QodanaLicenseOnlyToken) - if token == "" && licenseOnlyToken != "" { + if cloudUploadToken == "" && licenseOnlyToken != "" { Token = LicenseToken{ Token: licenseOnlyToken, LicenseOnly: true, } } else { Token = LicenseToken{ - Token: token, + Token: cloudUploadToken, LicenseOnly: false, } } diff --git a/cmd/cmd_test.go b/cmd/cmd_test.go index 9816313e..f9cc8882 100644 --- a/cmd/cmd_test.go +++ b/cmd/cmd_test.go @@ -25,7 +25,6 @@ import ( "github.com/JetBrains/qodana-cli/v2024/core" "github.com/JetBrains/qodana-cli/v2024/platform/msg" "github.com/JetBrains/qodana-cli/v2024/platform/product" - "github.com/JetBrains/qodana-cli/v2024/platform/qdenv" "github.com/JetBrains/qodana-cli/v2024/platform/qdyaml" "github.com/JetBrains/qodana-cli/v2024/platform/version" log "github.com/sirupsen/logrus" @@ -230,7 +229,7 @@ func TestAllCommandsWithContainer(t *testing.T) { token := os.Getenv("QODANA_LICENSE_ONLY_TOKEN") if //goland:noinspection GoBoolExpressions token == "" { - t.Skip("set your token here to run the test") + t.Skip("set your QODANA_LICENSE_ONLY_TOKEN as env variable to run the test") } if os.Getenv("GITHUB_ACTIONS") == "true" { @@ -273,7 +272,6 @@ func TestAllCommandsWithContainer(t *testing.T) { "-o", resultsPath, "--cache-dir", cachePath, "-v", filepath.Join(projectPath, ".idea") + ":/data/some", - "-e", qdenv.QodanaLicenseOnlyToken + "=" + os.Getenv("QODANA_LICENSE_ONLY_TOKEN"), "--fail-threshold", "5", "--print-problems", "--apply-fixes", diff --git a/cmd/init.go b/cmd/init.go index 38b55dac..62411bb8 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -99,13 +99,12 @@ func newInitCommand() *cobra.Command { "", "", os.Getenv(qdenv.QodanaToken), - os.Getenv(qdenv.QodanaLicenseOnlyToken), false, cliOptions.ProjectDir, cliOptions.ConfigName, ) if tokenloader.IsCloudTokenRequired(commonCtx, false) { - tokenloader.ValidateToken(commonCtx, cliOptions.Force) + tokenloader.ValidateCloudToken(commonCtx, cliOptions.Force) } }, } diff --git a/cmd/pull.go b/cmd/pull.go index bb0d6b6f..6162f513 100644 --- a/cmd/pull.go +++ b/cmd/pull.go @@ -47,7 +47,6 @@ func newPullCommand() *cobra.Command { "", "", os.Getenv(qdenv.QodanaToken), - os.Getenv(qdenv.QodanaLicenseOnlyToken), false, cliOptions.ProjectDir, cliOptions.ConfigName, diff --git a/cmd/scan.go b/cmd/scan.go index 95cf0dec..c5b0c0f7 100644 --- a/cmd/scan.go +++ b/cmd/scan.go @@ -59,7 +59,6 @@ But you can always override qodana.yaml options with the following command-line cliOptions.ResultsDir, cliOptions.ReportDir, platform.GetEnvWithOsEnv(cliOptions, qdenv.QodanaToken), - platform.GetEnvWithOsEnv(cliOptions, qdenv.QodanaLicenseOnlyToken), cliOptions.ClearCache, cliOptions.ProjectDir, cliOptions.ConfigName, diff --git a/cmd/send.go b/cmd/send.go index b91cd93d..d01cb345 100644 --- a/cmd/send.go +++ b/cmd/send.go @@ -52,7 +52,6 @@ If you are using other Qodana Cloud instance than https://qodana.cloud/, overrid cliOptions.ResultsDir, cliOptions.ReportDir, os.Getenv(qdenv.QodanaToken), - os.Getenv(qdenv.QodanaLicenseOnlyToken), false, cliOptions.ProjectDir, cliOptions.ConfigName, @@ -74,7 +73,7 @@ If you are using other Qodana Cloud instance than https://qodana.cloud/, overrid } platform.SendReport( publisher, - tokenloader.ValidateToken(commonCtx, false), + tokenloader.ValidateCloudToken(commonCtx, false), publisherPath, java, ) diff --git a/cmd/show.go b/cmd/show.go index b1d5f0ab..a0594708 100644 --- a/cmd/show.go +++ b/cmd/show.go @@ -45,7 +45,6 @@ This command serves the Qodana report locally and opens a browser to it.`, cliOptions.ResultsDir, cliOptions.ReportDir, os.Getenv(qdenv.QodanaToken), - os.Getenv(qdenv.QodanaLicenseOnlyToken), false, cliOptions.ProjectDir, cliOptions.ConfigName, diff --git a/core/container.go b/core/container.go index 13e3f700..791afdfd 100644 --- a/core/container.go +++ b/core/container.go @@ -275,6 +275,16 @@ func getDockerOptions(c corescan.Context) *backend.ContainerCreateConfig { updateScanContextEnv := func(key string, value string) { c = c.WithEnvExtractedFromOsEnv(key, value) } qdenv.ExtractQodanaEnvironment(updateScanContextEnv) + dockerEnv := c.Env() + qodanaCloudUploadToken := c.QodanaUploadToken() + if qodanaCloudUploadToken != "" { + dockerEnv = append(dockerEnv, fmt.Sprintf("%s=%s", qdenv.QodanaToken, qodanaCloudUploadToken)) + } + qodanaLicenseOnlyToken := os.Getenv(qdenv.QodanaLicenseOnlyToken) + if qodanaLicenseOnlyToken != "" && qodanaCloudUploadToken == "" { + dockerEnv = append(dockerEnv, fmt.Sprintf("%s=%s", qdenv.QodanaLicenseOnlyToken, qodanaLicenseOnlyToken)) + } + cachePath, err := filepath.Abs(c.CacheDir()) if err != nil { log.Fatal("couldn't get abs path for cache", err) @@ -370,7 +380,7 @@ func getDockerOptions(c corescan.Context) *backend.ContainerCreateConfig { Tty: msg.IsInteractive(), AttachStdout: true, AttachStderr: true, - Env: c.Env(), + Env: dockerEnv, User: c.User(), ExposedPorts: exposedPorts, }, diff --git a/core/core_test.go b/core/core_test.go index 5981b3c8..f6855754 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -942,17 +942,16 @@ func TestQodanaOptions_RequiresToken(t *testing.T) { t.Run( tt.name, func(t *testing.T) { initArgs := commoncontext.Context{ - Linter: tt.linter, - Ide: tt.ide, - IsClearCache: false, - CacheDir: "", - ProjectDir: "", - ResultsDir: "", - ReportDir: "", - QodanaSystemDir: "", - Id: "", - QodanaToken: "", - QodanaLicenseOnlyToken: "", + Linter: tt.linter, + Ide: tt.ide, + IsClearCache: false, + CacheDir: "", + ProjectDir: "", + ResultsDir: "", + ReportDir: "", + QodanaSystemDir: "", + Id: "", + QodanaToken: "", } if tt.name == qdenv.QodanaToken { @@ -1099,7 +1098,6 @@ func Test_Properties(t *testing.T) { resultsDir, "", "", - "", false, projectDir, "", @@ -1119,8 +1117,8 @@ func Test_Properties(t *testing.T) { }, commonCtx, startup.PreparedHost{ - IdeDir: "", - QodanaToken: "", + IdeDir: "", + QodanaUploadToken: "", Prod: product.Product{ BaseScriptName: "rider", Code: "QDNET", diff --git a/core/corescan/context.go b/core/corescan/context.go index b69daa3d..4f666800 100644 --- a/core/corescan/context.go +++ b/core/corescan/context.go @@ -61,8 +61,7 @@ type Context struct { ideDir string qodanaYaml qdyaml.QodanaYaml prod product.Product - qodanaToken string - qodanaLicenseOnlyToken string + qodanaUploadToken string projectDir string resultsDir string configDir string @@ -122,8 +121,7 @@ func (c Context) Id() string { return c.id } func (c Context) IdeDir() string { return c.ideDir } func (c Context) QodanaYaml() qdyaml.QodanaYaml { return c.qodanaYaml } func (c Context) Prod() product.Product { return c.prod } -func (c Context) QodanaToken() string { return c.qodanaToken } -func (c Context) QodanaLicenseOnlyToken() string { return c.qodanaLicenseOnlyToken } +func (c Context) QodanaUploadToken() string { return c.qodanaUploadToken } func (c Context) ProjectDir() string { return c.projectDir } func (c Context) ResultsDir() string { return c.resultsDir } func (c Context) ConfigDir() string { return c.configDir } @@ -183,8 +181,7 @@ type ContextBuilder struct { IdeDir string QodanaYaml qdyaml.QodanaYaml Prod product.Product - QodanaToken string - QodanaLicenseOnlyToken string + QodanaUploadToken string ProjectDir string ResultsDir string ConfigDir string @@ -246,8 +243,7 @@ func (b ContextBuilder) Build() Context { ideDir: b.IdeDir, qodanaYaml: b.QodanaYaml, prod: b.Prod, - qodanaToken: b.QodanaToken, - qodanaLicenseOnlyToken: b.QodanaLicenseOnlyToken, + qodanaUploadToken: b.QodanaUploadToken, projectDir: b.ProjectDir, resultsDir: b.ResultsDir, configDir: b.ConfigDir, diff --git a/core/corescan/create_context.go b/core/corescan/create_context.go index c2749b92..b06bab5c 100644 --- a/core/corescan/create_context.go +++ b/core/corescan/create_context.go @@ -53,8 +53,7 @@ func CreateContext( IdeDir: preparedHost.IdeDir, QodanaYaml: qodanaYaml, Prod: preparedHost.Prod, - QodanaToken: preparedHost.QodanaToken, - QodanaLicenseOnlyToken: commonCtx.QodanaLicenseOnlyToken, + QodanaUploadToken: preparedHost.QodanaUploadToken, ProjectDir: commonCtx.ProjectDir, ResultsDir: commonCtx.ResultsDir, ConfigDir: commonCtx.ConfDirPath(), diff --git a/core/startup/prepare.go b/core/startup/prepare.go index b6540abc..a4e785a7 100644 --- a/core/startup/prepare.go +++ b/core/startup/prepare.go @@ -42,15 +42,15 @@ const ( ) type PreparedHost struct { - IdeDir string - QodanaToken string - Prod product.Product + IdeDir string + QodanaUploadToken string + Prod product.Product } // PrepareHost gets the current user, creates the necessary folders for the analysis. func PrepareHost(commonCtx commoncontext.Context) PreparedHost { prod := product.Product{} - token := commonCtx.QodanaToken + cloudUploadToken := commonCtx.QodanaToken ideDir := "" if commonCtx.IsClearCache { @@ -99,23 +99,22 @@ func PrepareHost(commonCtx commoncontext.Context) PreparedHost { } ideDir = val } - prod, token = prepareLocalIdeSettingsAndGetQodanaCloudToken(commonCtx, ideDir) + prod, cloudUploadToken = prepareLocalIdeSettingsAndGetQodanaCloudUploadToken(commonCtx, ideDir) } - commonCtx.QodanaToken = token if tokenloader.IsCloudTokenRequired(commonCtx, prod.IsCommunity() || prod.IsEap) { - token = tokenloader.ValidateToken(commonCtx, false) + cloudUploadToken = tokenloader.ValidateCloudToken(commonCtx, false) } result := PreparedHost{ - IdeDir: ideDir, - QodanaToken: token, - Prod: prod, + IdeDir: ideDir, + QodanaUploadToken: cloudUploadToken, + Prod: prod, } return result } -func prepareLocalIdeSettingsAndGetQodanaCloudToken( +func prepareLocalIdeSettingsAndGetQodanaCloudUploadToken( commonCtx commoncontext.Context, ideDir string, ) (product.Product, string) { @@ -123,7 +122,7 @@ func prepareLocalIdeSettingsAndGetQodanaCloudToken( qdenv.ExtractQodanaEnvironment(qdenv.SetEnv) isTokenRequired := tokenloader.IsCloudTokenRequired(commonCtx, prod.IsEap || prod.IsCommunity()) - token := tokenloader.LoadCloudToken(commonCtx, false, isTokenRequired, true) + token := tokenloader.LoadCloudUploadToken(commonCtx, false, isTokenRequired, true) cloud.SetupLicenseToken(token) SetupLicenseAndProjectHash(prod, cloud.GetCloudApiEndpoints(), cloud.Token.Token) PrepareDirectories( diff --git a/platform/commoncontext/compute.go b/platform/commoncontext/compute.go index 6dfc9479..f7d28763 100644 --- a/platform/commoncontext/compute.go +++ b/platform/commoncontext/compute.go @@ -35,7 +35,6 @@ func Compute( resultsDirFromCliOptions string, reportDirFromCliOptions string, qodanaCloudToken string, - qodanaLicenseOnlyToken string, clearCache bool, projectDir string, qodanaYamlPath string, @@ -55,17 +54,16 @@ func Compute( reportDir := computeReportDir(reportDirFromCliOptions, resultsDir) commonCtx := Context{ - Linter: linter, - Ide: ide, - IsClearCache: clearCache, - CacheDir: cacheDir, - ProjectDir: projectDir, - ResultsDir: resultsDir, - QodanaSystemDir: systemDir, - ReportDir: reportDir, - Id: qodanaId, - QodanaToken: qodanaCloudToken, - QodanaLicenseOnlyToken: qodanaLicenseOnlyToken, + Linter: linter, + Ide: ide, + IsClearCache: clearCache, + CacheDir: cacheDir, + ProjectDir: projectDir, + ResultsDir: resultsDir, + QodanaSystemDir: systemDir, + ReportDir: reportDir, + Id: qodanaId, + QodanaToken: qodanaCloudToken, } return commonCtx } diff --git a/platform/commoncontext/context.go b/platform/commoncontext/context.go index ecb02dd6..2678dbc7 100644 --- a/platform/commoncontext/context.go +++ b/platform/commoncontext/context.go @@ -23,17 +23,16 @@ import ( ) type Context struct { - Linter string - Ide string - IsClearCache bool - CacheDir string - ProjectDir string - ResultsDir string - ReportDir string - QodanaSystemDir string - Id string - QodanaToken string - QodanaLicenseOnlyToken string + Linter string + Ide string + IsClearCache bool + CacheDir string + ProjectDir string + ResultsDir string + ReportDir string + QodanaSystemDir string + Id string + QodanaToken string } func (c Context) LogDir() string { @@ -59,10 +58,9 @@ func (c Context) GetLinterDir() string { CloudTokenLoader */ -func (c Context) GetQodanaToken() string { return c.QodanaToken } -func (c Context) GetQodanaLicenseOnlyToken() string { return c.QodanaLicenseOnlyToken } -func (c Context) GetId() string { return c.Id } -func (c Context) GetIde() string { return c.Ide } -func (c Context) GetLinter() string { return c.Linter } -func (c Context) GetProjectDir() string { return c.ProjectDir } -func (c Context) GetLogDir() string { return c.LogDir() } +func (c Context) GetQodanaToken() string { return c.QodanaToken } +func (c Context) GetId() string { return c.Id } +func (c Context) GetIde() string { return c.Ide } +func (c Context) GetLinter() string { return c.Linter } +func (c Context) GetProjectDir() string { return c.ProjectDir } +func (c Context) GetLogDir() string { return c.LogDir() } diff --git a/platform/env_provider.go b/platform/env_provider.go new file mode 100644 index 00000000..49d99130 --- /dev/null +++ b/platform/env_provider.go @@ -0,0 +1,43 @@ +/* + * Copyright 2021-2024 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package platform + +import ( + "os" + "strings" +) + +type EnvProvider interface { + Env() []string +} + +func GetEnv(provider EnvProvider, key string) string { + for _, e := range provider.Env() { + if strings.HasPrefix(e, key) { + return strings.TrimPrefix(e, key+"=") + } + } + return "" +} + +func GetEnvWithOsEnv(provider EnvProvider, key string) string { + envFromProvider := GetEnv(provider, key) + if envFromProvider != "" { + return envFromProvider + } + return os.Getenv(key) +} diff --git a/platform/files.go b/platform/files.go index b7eebb20..45b82a27 100644 --- a/platform/files.go +++ b/platform/files.go @@ -20,11 +20,9 @@ import ( "bytes" "fmt" log "github.com/sirupsen/logrus" - "os" "path" "path/filepath" "reflect" - "strings" "text/tabwriter" "unsafe" ) @@ -65,27 +63,6 @@ func LogContext(contextPointer any) { log.Debug(buffer.String()) } -type EnvProvider interface { - Env() []string -} - -func GetEnv(provider EnvProvider, key string) string { - for _, e := range provider.Env() { - if strings.HasPrefix(e, key) { - return strings.TrimPrefix(e, key+"=") - } - } - return "" -} - -func GetEnvWithOsEnv(provider EnvProvider, key string) string { - envFromProvider := GetEnv(provider, key) - if envFromProvider != "" { - return envFromProvider - } - return os.Getenv(key) -} - func ReportResultsPath(reportDir string) string { return filepath.Join(reportDir, "results") } diff --git a/platform/options_test.go b/platform/options_test.go index 54ffd682..03ad0796 100644 --- a/platform/options_test.go +++ b/platform/options_test.go @@ -67,7 +67,6 @@ func TestFetchAnalyzerSettings(t *testing.T) { "", "", "", - "", false, projectDir, "", @@ -100,7 +99,6 @@ func TestFetchAnalyzerSettings(t *testing.T) { "", "", "", - "", false, projectDir, "", @@ -133,7 +131,6 @@ func TestFetchAnalyzerSettings(t *testing.T) { "", "", "", - "", false, projectDir, fileName, diff --git a/platform/qdenv/qdenv.go b/platform/qdenv/qdenv.go index ae5f1ff9..bdbf57fd 100644 --- a/platform/qdenv/qdenv.go +++ b/platform/qdenv/qdenv.go @@ -27,6 +27,7 @@ import ( ) const ( + // !!! QODANA_LICENSE_ONLY_TOKEN is accesses only by env !!! QodanaLicenseOnlyToken = "QODANA_LICENSE_ONLY_TOKEN" QodanaToken = "QODANA_TOKEN" QodanaRemoteUrl = "QODANA_REMOTE_URL" diff --git a/platform/run.go b/platform/run.go index 8e8aefbf..94bfec88 100644 --- a/platform/run.go +++ b/platform/run.go @@ -52,7 +52,6 @@ func RunThirdPartyLinterAnalysis( cliOptions.ResultsDir, cliOptions.ReportDir, GetEnvWithOsEnv(cliOptions, qdenv.QodanaToken), - GetEnvWithOsEnv(cliOptions, qdenv.QodanaLicenseOnlyToken), cliOptions.ClearCache, cliOptions.ProjectDir, cliOptions.ConfigName, @@ -162,7 +161,7 @@ func correctInitArgsForThirdParty(commonCtx commoncontext.Context) (commoncontex func checkLinterLicense(loader tokenloader.CloudTokenLoader) thirdpartyscan.ThirdPartyStartupCloudData { licensePlan := cloud.CommunityLicensePlan - token := tokenloader.LoadCloudToken(loader, false, false, true) + token := tokenloader.LoadCloudUploadToken(loader, false, false, true) projectIdHash := "" cloud.SetupLicenseToken(token) if cloud.Token.Token != "" { diff --git a/platform/tokenloader/token_loader.go b/platform/tokenloader/token_loader.go index 10ed2d31..1e8e2731 100644 --- a/platform/tokenloader/token_loader.go +++ b/platform/tokenloader/token_loader.go @@ -35,7 +35,6 @@ const keyringDefaultService = "qodana-cli" type CloudTokenLoader interface { GetQodanaToken() string - GetQodanaLicenseOnlyToken() string GetId() string GetIde() string @@ -46,7 +45,7 @@ type CloudTokenLoader interface { } func IsCloudTokenRequired(tokenLoader CloudTokenLoader, forceIsCommunityOrEap bool) bool { - if tokenLoader.GetQodanaToken() != "" || tokenLoader.GetQodanaLicenseOnlyToken() != "" { + if tokenLoader.GetQodanaToken() != "" || os.Getenv(qdenv.QodanaLicenseOnlyToken) != "" { return true } @@ -73,10 +72,9 @@ func IsCloudTokenRequired(tokenLoader CloudTokenLoader, forceIsCommunityOrEap bo return false } -func LoadCloudToken(tokenLoader CloudTokenLoader, refresh bool, requiresToken bool, interactive bool) string { +func LoadCloudUploadToken(tokenLoader CloudTokenLoader, refresh bool, requiresToken bool, interactive bool) string { tokenFetchers := []func(bool) string{ func(_ bool) string { return tokenLoader.GetQodanaToken() }, - func(_ bool) string { return getTokenFromEnv() }, func(refresh bool) string { return getTokenFromKeychain(refresh, tokenLoader.GetId()) }, } if interactive && requiresToken { @@ -94,8 +92,8 @@ func LoadCloudToken(tokenLoader CloudTokenLoader, refresh bool, requiresToken bo return "" } -func ValidateToken(tokenLoader CloudTokenLoader, refresh bool) string { - token := LoadCloudToken(tokenLoader, refresh, true, true) +func ValidateCloudToken(tokenLoader CloudTokenLoader, refresh bool) string { + token := LoadCloudUploadToken(tokenLoader, refresh, true, true) if token != "" { ValidateTokenPrintProject(token) } @@ -178,15 +176,6 @@ func setupToken(path string, id string, logdir string) string { } } -func getTokenFromEnv() string { - tokenFromEnv := os.Getenv(qdenv.QodanaToken) - if tokenFromEnv != "" { - log.Debug("Loaded token from the environment variable") - return tokenFromEnv - } - return "" -} - func getTokenFromKeychain(refresh bool, id string) string { log.Debugf("project id: %s", id) if refresh || os.Getenv(qdenv.QodanaClearKeyring) != "" {