github.com/smartcontractkit/chainlink-testing-framework/libs@v0.0.0-20240227141906-ec710b4eb1a3/k8s/pkg/cdk8s/blockscout/blockscout.go (about)

     1  package blockscout
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  
     7  	"github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2"
     8  	"github.com/rs/zerolog/log"
     9  
    10  	"github.com/smartcontractkit/chainlink-testing-framework/libs/k8s/client"
    11  	"github.com/smartcontractkit/chainlink-testing-framework/libs/k8s/config"
    12  	"github.com/smartcontractkit/chainlink-testing-framework/libs/k8s/environment"
    13  	"github.com/smartcontractkit/chainlink-testing-framework/libs/k8s/imports/k8s"
    14  	"github.com/smartcontractkit/chainlink-testing-framework/libs/k8s/pkg"
    15  	a "github.com/smartcontractkit/chainlink-testing-framework/libs/k8s/pkg/alias"
    16  	"github.com/smartcontractkit/chainlink-testing-framework/libs/utils/ptr"
    17  )
    18  
    19  const (
    20  	URLsKey = "blockscout"
    21  )
    22  
    23  type Chart struct {
    24  	Props *Props
    25  }
    26  
    27  func (m Chart) IsDeploymentNeeded() bool {
    28  	return true
    29  }
    30  
    31  func (m Chart) GetName() string {
    32  	if m.Props == nil || m.Props.Name == "" {
    33  		return "blockscout"
    34  	}
    35  	return m.Props.Name
    36  }
    37  
    38  func (m Chart) GetProps() interface{} {
    39  	return m.Props
    40  }
    41  
    42  func (m Chart) GetPath() string {
    43  	return ""
    44  }
    45  
    46  func (m Chart) GetVersion() string {
    47  	return ""
    48  }
    49  
    50  func (m Chart) GetValues() *map[string]interface{} {
    51  	return nil
    52  }
    53  
    54  func (m Chart) ExportData(e *environment.Environment) error {
    55  	bsURL, err := e.Fwd.FindPort(
    56  		fmt.Sprintf("%s:0", m.GetName()),
    57  		fmt.Sprintf("%s-node", m.GetName()), "explorer").
    58  		As(client.LocalConnection, client.HTTP)
    59  	if err != nil {
    60  		return err
    61  	}
    62  	log.Info().Str("URL", bsURL).Msg("Blockscout explorer")
    63  	e.URLs[URLsKey] = []string{bsURL}
    64  	return nil
    65  }
    66  
    67  func New(props *Props) func(root cdk8s.Chart) environment.ConnectedChart {
    68  	return func(root cdk8s.Chart) environment.ConnectedChart {
    69  		dp := defaultProps()
    70  		config.MustMerge(dp, props)
    71  		c := &Chart{
    72  			Props: dp,
    73  		}
    74  		vars := vars{
    75  			Labels: &map[string]*string{
    76  				"app": ptr.Ptr(c.GetName()),
    77  			},
    78  			ConfigMapName: fmt.Sprintf("%s-cm", c.GetName()),
    79  			BaseName:      c.GetName(),
    80  			Port:          4000,
    81  			Props:         dp,
    82  		}
    83  		service(root, vars)
    84  		deployment(root, vars)
    85  		return c
    86  	}
    87  }
    88  
    89  type Props struct {
    90  	Name    string
    91  	HttpURL string `envconfig:"http_url"`
    92  	WsURL   string `envconfig:"ws_url"`
    93  }
    94  
    95  func defaultProps() *Props {
    96  	return &Props{
    97  		HttpURL: "http://geth:8544",
    98  		WsURL:   "ws://geth:8546",
    99  	}
   100  }
   101  
   102  // vars some shared labels/selectors and names that must match in resources
   103  type vars struct {
   104  	Labels        *map[string]*string
   105  	BaseName      string
   106  	ConfigMapName string
   107  	Port          float64
   108  	Props         *Props
   109  }
   110  
   111  func service(chart cdk8s.Chart, vars vars) {
   112  	k8s.NewKubeService(chart, ptr.Ptr(fmt.Sprintf("%s-service", vars.BaseName)), &k8s.KubeServiceProps{
   113  		Metadata: &k8s.ObjectMeta{
   114  			Name: ptr.Ptr(vars.BaseName),
   115  		},
   116  		Spec: &k8s.ServiceSpec{
   117  			Ports: &[]*k8s.ServicePort{
   118  				{
   119  					Name:       ptr.Ptr("explorer"),
   120  					Port:       ptr.Ptr(vars.Port),
   121  					TargetPort: k8s.IntOrString_FromNumber(ptr.Ptr[float64](4000)),
   122  				},
   123  			},
   124  			Selector: vars.Labels,
   125  		},
   126  	})
   127  }
   128  
   129  func postgresContainer(p vars) *k8s.Container {
   130  	internalRepo := os.Getenv(config.EnvVarInternalDockerRepo)
   131  	postgresRepo := "postgres"
   132  	if internalRepo != "" {
   133  		postgresRepo = fmt.Sprintf("%s/postgres", internalRepo)
   134  	}
   135  	return &k8s.Container{
   136  		Name:  ptr.Ptr(fmt.Sprintf("%s-db", p.BaseName)),
   137  		Image: ptr.Ptr(fmt.Sprintf("%s:13.6", postgresRepo)),
   138  		Ports: &[]*k8s.ContainerPort{
   139  			{
   140  				Name:          ptr.Ptr("postgres"),
   141  				ContainerPort: ptr.Ptr[float64](5432),
   142  			},
   143  		},
   144  		Env: &[]*k8s.EnvVar{
   145  			a.EnvVarStr("POSTGRES_PASSWORD", "postgres"),
   146  			a.EnvVarStr("POSTGRES_DB", "blockscout"),
   147  		},
   148  		LivenessProbe: &k8s.Probe{
   149  			Exec: &k8s.ExecAction{
   150  				Command: pkg.PGIsReadyCheck()},
   151  			InitialDelaySeconds: ptr.Ptr[float64](60),
   152  			PeriodSeconds:       ptr.Ptr[float64](60),
   153  		},
   154  		ReadinessProbe: &k8s.Probe{
   155  			Exec: &k8s.ExecAction{
   156  				Command: pkg.PGIsReadyCheck()},
   157  			InitialDelaySeconds: ptr.Ptr[float64](2),
   158  			PeriodSeconds:       ptr.Ptr[float64](2),
   159  		},
   160  		Resources: a.ContainerResources("1000m", "2048Mi", "1000m", "2048Mi"),
   161  	}
   162  }
   163  
   164  func deployment(chart cdk8s.Chart, vars vars) {
   165  	k8s.NewKubeDeployment(
   166  		chart,
   167  		ptr.Ptr(fmt.Sprintf("%s-deployment", vars.BaseName)),
   168  		&k8s.KubeDeploymentProps{
   169  			Metadata: &k8s.ObjectMeta{
   170  				Name: ptr.Ptr(vars.BaseName),
   171  			},
   172  			Spec: &k8s.DeploymentSpec{
   173  				Selector: &k8s.LabelSelector{
   174  					MatchLabels: vars.Labels,
   175  				},
   176  				Template: &k8s.PodTemplateSpec{
   177  					Metadata: &k8s.ObjectMeta{
   178  						Labels: vars.Labels,
   179  					},
   180  					Spec: &k8s.PodSpec{
   181  						ServiceAccountName: ptr.Ptr("default"),
   182  						Containers: &[]*k8s.Container{
   183  							container(vars),
   184  							postgresContainer(vars),
   185  						},
   186  					},
   187  				},
   188  			},
   189  		})
   190  }
   191  
   192  func container(vars vars) *k8s.Container {
   193  	internalRepo := os.Getenv(config.EnvVarInternalDockerRepo)
   194  	blockscoutRepo := "f4hrenh9it/blockscout"
   195  	if internalRepo != "" {
   196  		blockscoutRepo = fmt.Sprintf("%s/blockscout", internalRepo)
   197  	}
   198  	return &k8s.Container{
   199  		Name:            ptr.Ptr(fmt.Sprintf("%s-node", vars.BaseName)),
   200  		Image:           ptr.Ptr(fmt.Sprintf("%s:v1", blockscoutRepo)),
   201  		ImagePullPolicy: ptr.Ptr("Always"),
   202  		Command:         &[]*string{ptr.Ptr(`/bin/bash`)},
   203  		Args: &[]*string{
   204  			ptr.Ptr("-c"),
   205  			ptr.Ptr("mix ecto.create && mix ecto.migrate && mix phx.server"),
   206  		},
   207  		Ports: &[]*k8s.ContainerPort{
   208  			{
   209  				Name:          ptr.Ptr("explorer"),
   210  				ContainerPort: ptr.Ptr(vars.Port),
   211  			},
   212  		},
   213  		ReadinessProbe: &k8s.Probe{
   214  			HttpGet: &k8s.HttpGetAction{
   215  				Port: k8s.IntOrString_FromNumber(ptr.Ptr(vars.Port)),
   216  				Path: ptr.Ptr("/"),
   217  			},
   218  			InitialDelaySeconds: ptr.Ptr[float64](20),
   219  			PeriodSeconds:       ptr.Ptr[float64](5),
   220  		},
   221  		Env: &[]*k8s.EnvVar{
   222  			a.EnvVarStr("MIX_ENV", "prod"),
   223  			a.EnvVarStr("ECTO_USE_SSL", "'false'"),
   224  			a.EnvVarStr("COIN", "DAI"),
   225  			a.EnvVarStr("ETHEREUM_JSONRPC_VARIANT", "geth"),
   226  			a.EnvVarStr("ETHEREUM_JSONRPC_HTTP_URL", vars.Props.HttpURL),
   227  			a.EnvVarStr("ETHEREUM_JSONRPC_WS_URL", vars.Props.WsURL),
   228  			a.EnvVarStr("DATABASE_URL", "postgresql://postgres:@localhost:5432/blockscout?ssl=false"),
   229  		},
   230  		Resources: a.ContainerResources("2000m", "2048Mi", "2000m", "2048Mi"),
   231  	}
   232  }