internal/platform/qdcontainer/container.go (100 lines of code) (raw):

/* * 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 qdcontainer import ( "context" "fmt" "os" "runtime" "github.com/JetBrains/qodana-cli/internal/platform/msg" "github.com/docker/cli/cli/command" dockerCliConfig "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/flags" "github.com/docker/docker/api/types/system" "github.com/docker/docker/client" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" ) const ( MountDir = "/data/project" DataResultsDir = "/data/results" DataResultsReportDir = "/data/results/report" DataCacheDir = "/data/cache" DataCoverageDir = "/data/coverage" DataGlobalConfigDir = "/data/qodana-global-config/" // when container is launched by CLI, qodana-global-configurations.yaml file is mounted here ) func PrepareContainerEnvSettings() { ctx := context.Background() _, err := NewContainerClient(ctx) if err != nil { msg.ErrorMessage( "An error occured while connecting to Docker: %s\n"+ "Make sure that Docker or Podman is installed and a socket is available. If Docker is already "+ "running, consider setting DOCKER_HOST variable explicitly.", err, ) os.Exit(1) } checkEngineMemory() } // checkEngineMemory applicable only for Docker Desktop, // (has the default limit of 2GB which can be not enough when Gradle runs inside a container). func checkEngineMemory() { docker, err := NewContainerClient(context.Background()) if err != nil { log.Fatal(err) } goos := runtime.GOOS if //goland:noinspection GoBoolExpressions goos != "windows" && goos != "darwin" { return } info, err := docker.Info(context.Background()) if err != nil { log.Fatal(err) } var helpUrl string //goland:noinspection GoDfaConstantCondition switch goos { case "windows": helpUrl = "https://docs.docker.com/desktop/settings/windows/#advanced" case "darwin": helpUrl = "https://docs.docker.com/desktop/settings/mac/#advanced-1" } log.Debug("Docker memory limit is set to ", info.MemTotal/1024/1024, " MB") if info.MemTotal < 4*1024*1024*1024 { msg.WarningMessage( `The container daemon is running with less than 4GB of RAM. If you experience issues, consider increasing the container runtime memory limit. Refer to %s for more information. `, helpUrl, ) } } // NewContainerClient getContainerClient returns a docker client. func NewContainerClient(ctx context.Context) (client.APIClient, error) { logWarnWriter := log.StandardLogger().WriterLevel(log.WarnLevel) configFile := dockerCliConfig.LoadDefaultConfigFile(logWarnWriter) err := logWarnWriter.Close() if err != nil { log.Warnf("Failed to close log writer: %s", err) } clientOptions := flags.NewClientOptions() apiClient, err := command.NewAPIClientFromFlags(clientOptions, configFile) if err != nil { return nil, fmt.Errorf("failed to create Docker API client: %w", err) } apiClient.NegotiateAPIVersion(ctx) // A succesfull call to info is an indication that the client has connected to the socket successfully. info, err := apiClient.Info(ctx) if err != nil { return nil, fmt.Errorf("failed to initialize Docker API: %w", err) } logClientInfo(info) return apiClient, nil } func logClientInfo(info system.Info) { if log.GetLevel() < log.DebugLevel { return } marshalledInfo, err := yaml.Marshal(info) if err != nil { log.Errorf("Failed to print info from Docker API: %s", err) return } log.Debugf("Docker API client info:\n%s", marshalledInfo) }