github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/testutil/setup.go (about) 1 package testutil 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "net/http" 8 "net/url" 9 "os" 10 "path/filepath" 11 "strings" 12 "time" 13 14 "github.com/aws/aws-sdk-go-v2/aws" 15 awsconfig "github.com/aws/aws-sdk-go-v2/config" 16 "github.com/aws/aws-sdk-go-v2/credentials" 17 "github.com/aws/aws-sdk-go-v2/service/s3" 18 "github.com/deepmap/oapi-codegen/pkg/securityprovider" 19 "github.com/spf13/viper" 20 "github.com/treeverse/lakefs/pkg/api/apigen" 21 "github.com/treeverse/lakefs/pkg/api/apiutil" 22 "github.com/treeverse/lakefs/pkg/block" 23 "github.com/treeverse/lakefs/pkg/config" 24 "github.com/treeverse/lakefs/pkg/logging" 25 ) 26 27 const defaultSetupTimeout = 5 * time.Minute 28 29 type SetupTestingEnvParams struct { 30 Name string 31 StorageNS string 32 33 // Only if non-empty 34 AdminAccessKeyID string 35 AdminSecretAccessKey string 36 } 37 38 func SetupTestingEnv(params *SetupTestingEnvParams) (logging.Logger, apigen.ClientWithResponsesInterface, *s3.Client, string) { 39 logger := logging.ContextUnavailable() 40 viper.AddConfigPath(".") 41 viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) // support nested config 42 viper.SetEnvPrefix(strings.ToUpper(params.Name)) 43 viper.SetConfigName(strings.ToLower(params.Name)) 44 viper.AutomaticEnv() 45 46 viper.SetDefault("setup_lakefs", true) 47 viper.SetDefault("setup_lakefs_timeout", defaultSetupTimeout) 48 viper.SetDefault("endpoint_url", "http://localhost:8000") 49 viper.SetDefault("s3_endpoint", "s3.local.lakefs.io:8000") 50 viper.SetDefault("storage_namespace", fmt.Sprintf("s3://%s", params.StorageNS)) 51 viper.SetDefault(config.BlockstoreTypeKey, block.BlockstoreTypeS3) 52 viper.SetDefault("version", "dev") 53 currDir, err := os.Getwd() 54 if err != nil { 55 logger.WithError(err).Fatal("Failed to get CWD") 56 } 57 viper.SetDefault("glue_export_hooks_database", "export-hooks-esti") 58 viper.SetDefault("glue_export_region", "us-east-1") 59 viper.SetDefault("lakectl_dir", filepath.Join(currDir, "..")) 60 viper.SetDefault("azure_storage_account", "") 61 viper.SetDefault("azure_storage_access_key", "") 62 viper.SetDefault("large_object_path", "") 63 err = viper.ReadInConfig() 64 if err != nil && !errors.As(err, &viper.ConfigFileNotFoundError{}) { 65 logger.WithError(err).Fatal("Failed to read configuration") 66 } 67 68 ctx := context.Background() 69 70 // initialize the env/repo 71 logger = logging.ContextUnavailable() 72 logger.WithField("settings", viper.AllSettings()).Info(fmt.Sprintf("Starting %s", params.Name)) 73 74 endpointURL := ParseEndpointURL(logger, viper.GetString("endpoint_url")) 75 76 client, err := apigen.NewClientWithResponses(endpointURL) 77 if err != nil { 78 logger.WithError(err).Fatal("could not initialize API client") 79 } 80 81 if err := waitUntilLakeFSRunning(ctx, logger, client); err != nil { 82 logger.WithError(err).Fatal("Waiting for lakeFS") 83 } 84 85 setupLakeFS := viper.GetBool("setup_lakefs") 86 if setupLakeFS { 87 // first setup of lakeFS 88 mockEmail := "test@acme.co" 89 _, err := client.SetupCommPrefsWithResponse(context.Background(), apigen.SetupCommPrefsJSONRequestBody{ 90 Email: &mockEmail, 91 FeatureUpdates: false, 92 SecurityUpdates: false, 93 }) 94 if err != nil { 95 logger.WithError(err).Fatal("Failed to setup lakeFS") 96 } 97 adminUserName := params.Name 98 requestBody := apigen.SetupJSONRequestBody{ 99 Username: adminUserName, 100 } 101 if params.AdminAccessKeyID != "" || params.AdminSecretAccessKey != "" { 102 requestBody.Key = &apigen.AccessKeyCredentials{ 103 AccessKeyId: params.AdminAccessKeyID, 104 SecretAccessKey: params.AdminSecretAccessKey, 105 } 106 } 107 res, err := client.SetupWithResponse(ctx, requestBody) 108 if err != nil { 109 logger.WithError(err).Fatal("Failed to setup lakeFS") 110 } 111 if res.StatusCode() != http.StatusOK { 112 logger.WithField("status", res.HTTPResponse.Status).Fatal("Failed to setup lakeFS") 113 } 114 logger.Info("Cluster setup successfully") 115 credentialsWithSecret := res.JSON200 116 viper.Set("access_key_id", credentialsWithSecret.AccessKeyId) 117 viper.Set("secret_access_key", credentialsWithSecret.SecretAccessKey) 118 } else { 119 viper.Set("access_key_id", params.AdminAccessKeyID) 120 viper.Set("secret_access_key", params.AdminSecretAccessKey) 121 } 122 123 key := viper.GetString("access_key_id") 124 secret := viper.GetString("secret_access_key") 125 client, err = NewClientFromCreds(logger, key, secret, endpointURL) 126 if err != nil { 127 logger.WithError(err).Fatal("could not initialize API client with security provider") 128 } 129 130 s3Endpoint := viper.GetString("s3_endpoint") 131 svc, err := SetupTestS3Client(s3Endpoint, key, secret) 132 if err != nil { 133 logger.WithError(err).Fatal("could not initialize S3 client") 134 } 135 return logger, client, svc, endpointURL 136 } 137 138 func SetupTestS3Client(endpoint, key, secret string) (*s3.Client, error) { 139 if !strings.HasPrefix(endpoint, "http") { 140 endpoint = "http://" + endpoint 141 } 142 cfg, err := awsconfig.LoadDefaultConfig(context.Background(), 143 awsconfig.WithRegion("us-east-1"), 144 awsconfig.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(key, secret, "")), 145 ) 146 if err != nil { 147 return nil, err 148 } 149 forcePathStyleS3Client := viper.GetBool("force_path_style") 150 svc := s3.NewFromConfig(cfg, func(options *s3.Options) { 151 options.BaseEndpoint = aws.String(endpoint) 152 options.UsePathStyle = forcePathStyleS3Client 153 }) 154 return svc, nil 155 } 156 157 // ParseEndpointURL parses the given endpoint string 158 func ParseEndpointURL(logger logging.Logger, endpointURL string) string { 159 u, err := url.Parse(endpointURL) 160 if err != nil { 161 logger.WithError(err).Fatal("could not initialize API client with security provider") 162 } 163 if u.Path == "" || u.Path == "/" { 164 endpointURL = strings.TrimRight(endpointURL, "/") + apiutil.BaseURL 165 } 166 167 return endpointURL 168 } 169 170 // NewClientFromCreds creates a client using the credentials of a user 171 func NewClientFromCreds(logger logging.Logger, accessKeyID string, secretAccessKey string, endpointURL string) (*apigen.ClientWithResponses, error) { 172 basicAuthProvider, err := securityprovider.NewSecurityProviderBasicAuth(accessKeyID, secretAccessKey) 173 if err != nil { 174 logger.WithError(err).Fatal("could not initialize basic auth security provider") 175 } 176 177 return apigen.NewClientWithResponses(endpointURL, apigen.WithRequestEditorFn(basicAuthProvider.Intercept)) 178 } 179 180 const checkIteration = 5 * time.Second 181 182 func waitUntilLakeFSRunning(ctx context.Context, logger logging.Logger, cl apigen.ClientWithResponsesInterface) error { 183 setupCtx, cancel := context.WithTimeout(ctx, viper.GetDuration("setup_lakefs_timeout")) 184 defer cancel() 185 for { 186 _, err := cl.HealthCheckWithResponse(setupCtx) 187 if err == nil { 188 return nil 189 } 190 logger.WithError(err).Info("Setup failed") 191 192 select { 193 case <-setupCtx.Done(): 194 return setupCtx.Err() 195 case <-time.After(checkIteration): 196 } 197 } 198 }