package opensearch import ( "context" "encoding/json" "fmt" "io" "time" "github.com/docker/go-units" "github.com/moby/moby/api/types/container" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" ) const ( defaultPassword = "admin" defaultUsername = "admin" defaultHTTPPort = "9200/tcp" ) // OpenSearchContainer represents the OpenSearch container type used in the module type OpenSearchContainer struct { testcontainers.Container User string Password string } // Deprecated: use Run instead // RunContainer creates an instance of the OpenSearch container type func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomizer) (*OpenSearchContainer, error) { return Run(ctx, "opensearchproject/opensearch:2.11.1", opts...) } // Run creates an instance of the OpenSearch container type func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (*OpenSearchContainer, error) { // Gather all config options (defaults and then apply provided options) settings := defaultOptions() for _, opt := range opts { if apply, ok := opt.(Option); ok { if err := apply(settings); err != nil { return nil, fmt.Errorf("apply option: %w", err) } } } username := settings.Username password := settings.Password moduleOpts := make([]testcontainers.ContainerCustomizer, 0, 4+len(opts)) moduleOpts = append(moduleOpts, testcontainers.WithEnv(map[string]string{ "discovery.type": "single-node", "DISABLE_INSTALL_DEMO_CONFIG": "true", "DISABLE_SECURITY_PLUGIN": "true", "OPENSEARCH_USERNAME": username, "OPENSEARCH_PASSWORD": password, }), testcontainers.WithExposedPorts(defaultHTTPPort, "9600/tcp"), testcontainers.WithHostConfigModifier(func(hc *container.HostConfig) { hc.Ulimits = []*units.Ulimit{ { Name: "memlock", Soft: -1, // Set memlock to unlimited (no soft or hard limit) Hard: -1, }, { Name: "nofile", Soft: 65536, // Maximum number of open files for the opensearch user - set to at least 65536 Hard: 65536, }, } }), // the wait strategy does not support TLS at the moment, // so we need to disable it in the strategy for now. testcontainers.WithWaitStrategy(wait.ForHTTP("/"). WithPort("9200"). WithTLS(false). WithStartupTimeout(120*time.Second). WithStatusCodeMatcher(func(status int) bool { return status == 200 }). WithBasicAuth(username, password). WithResponseMatcher(func(body io.Reader) bool { bs, err := io.ReadAll(body) if err != nil { return false } type response struct { Tagline string `json:"tagline"` } var r response err = json.Unmarshal(bs, &r) if err != nil { return false } return r.Tagline == "The OpenSearch Project: https://opensearch.org/" })), ) moduleOpts = append(moduleOpts, opts...) ctr, err := testcontainers.Run(ctx, img, moduleOpts...) var c *OpenSearchContainer if ctr != nil { c = &OpenSearchContainer{Container: ctr, User: username, Password: password} } if err != nil { return c, fmt.Errorf("run opensearch: %w", err) } return c, nil } // Address retrieves the address of the OpenSearch container. // It will use http as protocol, as TLS is not supported at the moment. func (c *OpenSearchContainer) Address(ctx context.Context) (string, error) { return c.PortEndpoint(ctx, defaultHTTPPort, "http") }