Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[infra-proxy-service] Add integration testing for infra-proxy-service #4690

Merged
merged 16 commits into from
Feb 10, 2021
Merged
17 changes: 17 additions & 0 deletions .expeditor/verify_private.pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,23 @@ steps:
- HAB_STUDIO_SUP=false
- HAB_NONINTERACTIVE=true

- label: "infra-proxy-service"
command:
- . scripts/verify_setup.sh
- hab studio run "source scripts/verify_studio_init.sh && start_deployment_service && chef-automate dev deployinate && infra_service_integration"
timeout_in_minutes: 20
retry:
automatic:
limit: 1
expeditor:
executor:
docker:
privileged: true
environment:
- HAB_STUDIO_SUP=false
- HAB_NONINTERACTIVE=true
- CONTAINER_HOSTNAME=localhost

#
# The following tests all use the integration test framework for
# end-to-end testing. These tests all test full deployments of
Expand Down
24 changes: 20 additions & 4 deletions .studio/infra-proxy-service
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ function start_infra_proxy_service() {
chef-automate dev deploy-some $HAB_ORIGIN/infra-proxy-service --with-deps
}

document "infra_service_integration" <<DOC
run the infra service's integration tests
DOC
function infra_service_integration() {
check_if_deployinate_started || return 1;
export CONTAINER_HOSTNAME=localhost
# Setup local chef server
# loads data which resides in dev-docs/adding-data/infra/chef-repo
infra_service_load_chef_repo
A2_SVC_NAME="infra-proxy-service" A2_SVC_PATH="/hab/svc/infra-proxy-service" go_test "github.com/chef/automate/components/infra-proxy-service/integration_test"
}

document "infra_service_load_sample_data" <<DOC
Before running this command make sure either run 'start_infra_proxy_service' or 'start_all_services'
Hits the create server & org endpoint inside the infra-proxy-service. (CreateServer & CreateOrg)
Expand Down Expand Up @@ -73,17 +85,17 @@ function infra_service_load_sample_data() {
}

function infra_service_create_servers_orgs() {
chef-automate dev grpcurl automate-gateway -- -d \
chef-automate dev grpcurl infra-proxy-service -- -d \
"$(cat << EOF
{"id": "$1", "name": "$2", "ip_address": "$3", "fqdn": "$4"}
EOF
)" chef.automate.api.infra_proxy.InfraProxy.CreateServer >/dev/null
)" chef.automate.domain.infra_proxy.service.InfraProxyService.CreateServer >/dev/null

chef-automate dev grpcurl automate-gateway -- -d \
chef-automate dev grpcurl infra-proxy-service -- -d \
"$(cat << EOF
{"id": "$5", "name": "$6", "admin_user": "$7", "admin_key": "$8", "server_id": "$1", "projects": []}
EOF
)" chef.automate.api.infra_proxy.InfraProxy.CreateOrg >/dev/null
)" chef.automate.domain.infra_proxy.service.InfraProxyService.CreateOrg >/dev/null
}

function infra_service_load_chef_repo() {
Expand Down Expand Up @@ -159,6 +171,10 @@ EOH
source "https://supermarket.chef.io"

cookbook "audit"
cookbook "chef-client"
cookbook "iptables"
cookbook "netdev"
cookbook "sudo"
EOH

if ! hab pkg exec chef/chef-dk knife opc user list -c ${chef_server_pivotal_rb_path} | grep ${chef_server_test_node_name} &> /dev/null; then
Expand Down
2 changes: 1 addition & 1 deletion components/infra-proxy-service/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
include ../../Makefile.common_go

PACKAGE_PATH = github.com/chef/automate/components/infra-proxy-service
PACKAGE_PATH = github.com/chef/automate/components/infra-proxy-service/server

BINS = ${PACKAGE_PATH}/cmd/infra-proxy-service
MIGRATION_READMES = storage/postgres/migration/sql/README.md
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import (

secrets "github.com/chef/automate/api/external/secrets"
"github.com/chef/automate/api/interservice/authz"
"github.com/chef/automate/components/infra-proxy-service/config"
"github.com/chef/automate/components/infra-proxy-service/server"
"github.com/chef/automate/components/infra-proxy-service/service"
"github.com/chef/automate/components/infra-proxy-service/storage/postgres/migration"
"github.com/chef/automate/lib/grpc/secureconn"
"github.com/chef/automate/lib/logger"
platform_config "github.com/chef/automate/lib/platform/config"
"github.com/chef/automate/lib/tls/certs"
)

var serveCmd = &cobra.Command{
Expand All @@ -27,18 +27,6 @@ var serveCmd = &cobra.Command{
Args: cobra.ExactArgs(1),
}

type config struct {
GRPC string `mapstructure:"grpc"`
LogFormat string `mapstructure:"log-format"`
LogLevel string `mapstructure:"log-level"`
certs.TLSConfig `mapstructure:"tls"`
PGURL string `mapstructure:"pg_url"`
Database string `mapstructure:"database"`
MigrationsPath string `mapstructure:"migrations-path"`
AuthzAddress string `mapstructure:"authz-address"`
SecretsAddress string `mapstructure:"secrets-address"`
}

func serve(cmd *cobra.Command, args []string) {
cmd.PersistentFlags().StringP("log-level", "l", "info", "log level")
cmd.PersistentFlags().StringP("log-format", "f", "text", "log format")
Expand All @@ -53,7 +41,7 @@ func serve(cmd *cobra.Command, args []string) {
fail(errors.Wrap(err, `Could not read config file. Please pass a config file as the only argument to this command.`))
}

cfg := config{}
cfg := config.Service{}
if err := viper.Unmarshal(&cfg); err != nil {
fail(errors.Wrap(err, "couldn't parse configuration file"))
}
Expand Down
124 changes: 124 additions & 0 deletions components/infra-proxy-service/config/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package config

import (
"fmt"
"net/url"
"os"

"github.com/pkg/errors"
"github.com/spf13/viper"

secrets "github.com/chef/automate/api/external/secrets"
"github.com/chef/automate/api/interservice/authz"
"github.com/chef/automate/components/infra-proxy-service/service"
"github.com/chef/automate/components/infra-proxy-service/storage/postgres/migration"
"github.com/chef/automate/lib/grpc/secureconn"
"github.com/chef/automate/lib/logger"
platform_config "github.com/chef/automate/lib/platform/config"
"github.com/chef/automate/lib/tls/certs"
)

// Service config options
type Service struct {
GRPC string `mapstructure:"grpc"`
LogFormat string `mapstructure:"log-format"`
LogLevel string `mapstructure:"log-level"`
certs.TLSConfig `mapstructure:"tls"`
PGURL string `mapstructure:"pg_url"`
Database string `mapstructure:"database"`
MigrationsPath string `mapstructure:"migrations-path"`
AuthzAddress string `mapstructure:"authz-address"`
SecretsAddress string `mapstructure:"secrets-address"`
}

// ConfigFromViper returns a Service instance from the current viper config
func ConfigFromViper(configFile string) (*service.Service, error) {
// Set the file name of the configurations file
viper.SetConfigName("config")
// Set the configuration file type
viper.SetConfigType("yaml")
// Set the path to look for the configurations file
viper.AddConfigPath("../dev")

if err := viper.ReadInConfig(); err != nil {
fail(errors.Wrap(err, `Could not read config file. Please pass a config file as the only argument to this command.`))
}

cfg := Service{}
if err := viper.Unmarshal(&cfg); err != nil {
fail(errors.Wrap(err, "couldn't parse configuration file"))
}

pgURL, err := platform_config.PGURIFromEnvironment(cfg.Database)
if err != nil {
fail(errors.Wrap(err, "Failed to get pg uri"))
}
cfg.PGURL = pgURL

l, err := logger.NewLogger(cfg.LogFormat, cfg.LogLevel)
if err != nil {
fail(errors.Wrap(err, "couldn't initialize logger"))
}

cfg.FixupRelativeTLSPaths(configFile)
serviceCerts, err := cfg.ReadCerts()
if err != nil {
fail(errors.Wrap(err, "Could not read certs"))
}
connFactory := secureconn.NewFactory(*serviceCerts)

mustBeADirectory(cfg.MigrationsPath)
u, err := url.Parse(cfg.PGURL)
if err != nil {
fail(errors.Wrapf(err, "could not parse pg_url %s from config", cfg.PGURL))
}
migrationConfig := migration.Config{
Path: cfg.MigrationsPath,
PGURL: u,
Logger: l,
}

if cfg.AuthzAddress == "" {
fail(errors.New("missing required config authz_address"))
}
authzConn, err := connFactory.Dial("authz-service", cfg.AuthzAddress)
if err != nil {
fail(errors.Wrapf(err, "failed to dial authz-service at (%s)", cfg.AuthzAddress))
}
authzClient := authz.NewAuthorizationServiceClient(authzConn)

if cfg.SecretsAddress == "" {
fail(errors.New("missing required config secrets_address"))
}
secretsConn, err := connFactory.Dial("secrets-service", cfg.SecretsAddress)
if err != nil {
fail(errors.Wrapf(err, "failed to dial secrets-service at (%s)", cfg.SecretsAddress))
}

// gets secrets client
secretsClient := secrets.NewSecretsServiceClient(secretsConn)

service, err := service.Start(l, migrationConfig, connFactory, secretsClient, authzClient)
if err != nil {
fail(errors.Wrap(err, "could not initialize storage"))
}

return service, nil
}

// fail outputs the error and exits with a non-zero code
func fail(err error) {
// no error check: if this goes wrong, we're in trouble anyways
fmt.Fprint(os.Stderr, err.Error()) // nolint: gas
os.Exit(1)
}

func mustBeADirectory(path string) {
stat, err := os.Stat(path)
if err == nil && stat.IsDir() {
return // everything's in its right place
} else if err != nil {
fail(errors.Wrapf(err, "open path %#v", path))
}
fail(fmt.Errorf("path %#v is not a directory", path))
}
10 changes: 5 additions & 5 deletions components/infra-proxy-service/dev/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ log-level: "info"
log-format: "text"

tls:
cert_path: ../../../dev/certs/infra-proxy-service.crt
key_path: ../../../dev/certs/infra-proxy-service.key
root_ca_path: ../../../dev/certs/Chef_Automate_FAKE_Dev.crt
cert_path: /hab/svc/infra-proxy-service/config/service.crt
key_path: /hab/svc/infra-proxy-service/config/service.key
root_ca_path: /hab/svc/infra-proxy-service/config/root_ca.crt

pg_url: "postgresql://[email protected]:5432/infra_proxy_test?sslmode=disable"
database: infra_proxy_test
migrations-path: storage/postgres/migration/sql/
database: chef_infra_proxy
migrations-path: /src/components/infra-proxy-service/storage/postgres/migration/sql/
authz-address: "0.0.0.0:10130"
secrets-address: "0.0.0.0:10131"
36 changes: 36 additions & 0 deletions components/infra-proxy-service/integration_test/clients_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package integration_test

import (
"context"
"testing"

"github.com/chef/automate/api/interservice/infra_proxy/request"
"github.com/stretchr/testify/assert"
)

func TestGetClients(t *testing.T) {
// rpc GetClients (request.Clients) returns (response.Clients)
ctx := context.Background()
req := &request.Clients{
ServerId: autoDeployedChefServerID,
OrgId: autoDeployedChefOrganizationID,
}
res, err := infraProxy.GetClients(ctx, req)
assert.NoError(t, err)
assert.NotNil(t, res)
assert.Equal(t, 3, len(res.GetClients()))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests assume that we have a predefined test data loaded to the server. This is fine at this point since we do not have the API to add the data. But this needs to be addressed later.

}

func TestGetClient(t *testing.T) {
// rpc GetClient (request.Client) returns (response.Client)
ctx := context.Background()
req := &request.Client{
ServerId: autoDeployedChefServerID,
OrgId: autoDeployedChefOrganizationID,
Name: "chef-load-1",
}
res, err := infraProxy.GetClient(ctx, req)
assert.NoError(t, err)
assert.NotNil(t, res)
assert.Equal(t, "chef-load-1", res.Name)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

}
33 changes: 33 additions & 0 deletions components/infra-proxy-service/integration_test/global_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package integration_test

import (
"os"
"testing"
)

// Global variables
var (
autoDeployedChefServerID = "local-dev"
autoDeployedChefOrganizationID = "test-org"
cFile = "/src/components/infra-proxy-service/dev/config"
// This suite variable will be available for every single test as long as they
// belong to the 'integration_test' package.
suite = NewSuite()
)

func TestMain(m *testing.M) {
// Global Setup hook: Here is where you can initialize anythings you need
// for your tests to run, things like; Initialize ES indices, insert
// nodes or runs, etc.
suite.GlobalSetup()

// Execute the test suite and record the exit code
exitCode := m.Run()

// Teardown hook: It says it all, this hook should clean documents
// from ES so that the next test can run on a clean env.
suite.GlobalTeardown()

// call with result of m.Run()
os.Exit(exitCode)
}
52 changes: 52 additions & 0 deletions components/infra-proxy-service/integration_test/suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package integration_test

import (
"github.com/chef/automate/components/infra-proxy-service/config"
"github.com/chef/automate/components/infra-proxy-service/server"
)

// Global variables
var (
// A global Infra Proxy Server instance to call any rpc function
//
// From any test you can directly call:
// ```
// res, err := infraProxy.GetOrgs(ctx, &req)
// ```
infraProxy *server.Server
)

type Suite struct{}

// Just returns a new struct. You have to call GlobalSetup() to setup
func NewSuite() *Suite {
return new(Suite)
}

// GlobalSetup makes all connections.
func (s *Suite) GlobalSetup() error {
var err error
// set global infraProxy
infraProxy, err = newInfraProxyServer()

if err != nil {
return err
}

return nil
}

// GlobalTeardown is the place where you tear everything down after we have finished
func (s *Suite) GlobalTeardown() {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please confirm if we need to close any open connections like authn or authz.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I can check in this method we have performed some data cleanup like Postgres data, elastic search indices, etc, not the service connections. will investigate more on this!
Thanks!


// newInfraProxyServer initializes a InfraProxyServer with the default config
func newInfraProxyServer() (*server.Server, error) {
service, err := config.ConfigFromViper(cFile)
if err != nil {
return nil, err
}

gRPC := server.NewServer(service)

return gRPC, nil
}
Loading