diff --git a/Makefile b/Makefile index b2abfc0..5eb5f64 100755 --- a/Makefile +++ b/Makefile @@ -1,50 +1,52 @@ CLIENT_APP_NAME := container-bridge-client AGENT_APP_NAME := container-bridge-agent -BUILD := 0.0.1 +BUILD := 0.1.1 -TOOLS_DIR := .tools/ -GOLANGCI_LINT := ${TOOLS_DIR}github.com/golangci/golangci-lint/cmd/golangci-lint@v1.46.0 -OAPI_CODEGEN := ${TOOLS_DIR}github.com/deepmap/oapi-codegen/cmd/oapi-codegen@latest -GOIMPORTS := ${TOOLS_DIR}mvdan.cc/gofumpt/gofumports@v0.1.1 +OPEN_API_CODEGEN := github.com/deepmap/oapi-codegen/cmd/oapi-codegen@latest -${OAPI_CODEGEN} ${GOLANGCI_LINT} ${GOIMPORTS}: +${OPEN_API_CODEGEN}: $(eval TOOL=$(@:%=%)) @echo Installing ${TOOL}... - go install $(TOOL:${TOOLS_DIR}%=%) - @mkdir -p $(dir ${TOOL}) - @cp ${GOBIN}/$(firstword $(subst @, ,$(notdir ${TOOL}))) ${TOOL} + go install $(@:%=%) -tools: ${OAPI_CODEGEN} ${GOLANGCI_LINT} ${GOIMPORTS} +tools: ${OPEN_API_CODEGEN} -OAPI_DIR = ./api +OPEN_API_DIR = ./api oapi-gen: tools oapi-gen-agent oapi-gen-client oapi-gen-agent: $(eval APP_NAME=agent) @echo Generating server for ${APP_NAME} - @mkdir -p ${APP_NAME}/${OAPI_DIR} - ${OAPI_CODEGEN} -config ./${APP_NAME}/cfg.yaml ./${APP_NAME}/openapi.yaml + @mkdir -p ${APP_NAME}/${OPEN_API_DIR} + ${GOBIN}/oapi-codegen -config ./${APP_NAME}/cfg.yaml ./${APP_NAME}/openapi.yaml oapi-gen-client: $(eval APP_NAME=client) @echo Generating server for ${APP_NAME} - @mkdir -p ${APP_NAME}/${OAPI_DIR} - ${OAPI_CODEGEN} -config ./${APP_NAME}/cfg.yaml ./${APP_NAME}/openapi.yaml + @mkdir -p ${APP_NAME}/${OPEN_API_DIR} + ${GOBIN}/oapi-codegen -config ./${APP_NAME}/cfg.yaml ./${APP_NAME}/openapi.yaml -start-docker-compose: +start-docker-compose-test: docker compose up -d --no-recreate + go test -timeout 120s -run ^Test* github.com/kube-tarian/container-bridge/integration_tests -v -stop-docker-compose: +stop-docker-compose-test: docker compose down -v build: - go mod vendor - go build -o build/client client/main.go - go build -o build/agent agent/main.go + go mod download + CGO_ENABLED=0 go build -o build/client client/main.go + CGO_ENABLED=0 go build -o build/agent agent/main.go clean: rm -rf build docker-build: - docker build -f Dockerfile.client -t ${CLIENT_APP_NAME}:${BUILD} . - docker build -f Dockerfile.agent -t ${AGENT_APP_NAME}:${BUILD} . + docker build -f dockerfiles/client/Dockerfile -t ${CLIENT_APP_NAME}:${BUILD} . + docker build -f dockerfiles/agent/Dockerfile -t ${AGENT_APP_NAME}:${BUILD} . + +start-manual-test: + docker compose -f ./docker-compose_manual_test.yaml up -d --no-recreate + +stop-manual-test: + docker compose -f ./docker-compose_manual_test.yaml down -v diff --git a/Notes.md b/Notes.md index ebf7514..7a0f93c 100644 --- a/Notes.md +++ b/Notes.md @@ -1,16 +1,26 @@ # Container brdige -## Start docker compose +## Chart and docker versioning + +The following files to be updated for chart and docker tag versionsing: +- Makefile -> BUILD parameter +- charts/agent/Chart.yaml +- charts/client/Chart.yaml +- .github/workflows/agent-docker-image.yaml +- .github/workflows/client-docker-image.yaml + +Currently docker images are tagged latest always. Once stabilized versioning can be added to workflow with same version as chart by modifying above 3 files. + +## Start docker compose manual test ``` -export GITLAB_HOME=./gitlab-repo-server -docker compose up -d +docker compose -f ./docker-compose_manual_test.yaml up -d ``` ## Stop docker compose ``` -docker compose down -v +docker compose -f ./docker-compose_manual_test.yaml down -v ``` ## Docker registry configuration @@ -93,4 +103,67 @@ So have to tag with localhost:5001 prefix path - Add data source for clickhouse -- \ No newline at end of file +# local environment with cpu&mem + +## Docker + +Reference: https://docs.docker.com/config/containers/resource_constraints/ + +``` +docker run -it --cpus="0.5" --memory=256m container-bridge-agent:0.1.1 +docker run -it --cpus="0.5" --memory=256m container-bridge-client:0.1.1 +``` + +## Docker compose + +Reference: https://docs.docker.com/compose/compose-file/compose-file-v3/#resources + +``` +For example: +version: "3.9" +services: + agent: + image: container-bridge-agent:0.1.1 + deploy: + resources: + limits: + cpus: '0.50' + memory: 256M + reservations: + cpus: '0.25' + memory: 256M +``` + +# Example docker event payload + +``` +{ + "events": [ + { + "id": "d539f3c5-5734-47f0-a2a8-d27de5f9edb1", + "timestamp": "2022-09-21T19:00:49.658010356Z", + "action": "push", + "target": { + "mediaType": "application/octet-stream", + "size": 29136663, + "digest": "sha256:12f42424f10d587d02674c8a0dab1c08d3fd81ab6bac5b7f5e3799215c6c52e6", + "length": 29136663, + "repository": "ubuntu", + "url": "http://localhost:5001/v2/ubuntu/blobs/sha256:12f42424f10d587d02674c8a0dab1c08d3fd81ab6bac5b7f5e3799215c6c52e6" + }, + "request": { + "id": "3784878a-5075-4ea5-a51b-5288ebc54c9c", + "addr": "172.22.0.1:36222", + "host": "localhost:5001", + "method": "PUT", + "useragent": "containers/5.16.0 (github.com/containers/image)" + }, + "actor": {}, + "source": { + "addr": "5b458294602c:5000", + "instanceID": "da4bdd5f-3f85-45c6-a37d-bc97a82fbd39" + } + } + ] +} +``` diff --git a/agent/api/agent.gen.go b/agent/api/agent.gen.go index 538d653..53d5f24 100644 --- a/agent/api/agent.gen.go +++ b/agent/api/agent.gen.go @@ -23,8 +23,8 @@ type ServerInterface interface { // (GET /api-docs) GetApiDocs(w http.ResponseWriter, r *http.Request) // Post Docker artifactory events - // (POST /localregistry/event/docker) - PostLocalregistryEventDocker(w http.ResponseWriter, r *http.Request) + // (POST /event/docker) + PostEventDocker(w http.ResponseWriter, r *http.Request) // Kubernetes readiness and liveness probe endpoint // (GET /status) GetStatus(w http.ResponseWriter, r *http.Request) @@ -54,12 +54,12 @@ func (siw *ServerInterfaceWrapper) GetApiDocs(w http.ResponseWriter, r *http.Req handler(w, r.WithContext(ctx)) } -// PostLocalregistryEventDocker operation middleware -func (siw *ServerInterfaceWrapper) PostLocalregistryEventDocker(w http.ResponseWriter, r *http.Request) { +// PostEventDocker operation middleware +func (siw *ServerInterfaceWrapper) PostEventDocker(w http.ResponseWriter, r *http.Request) { ctx := r.Context() var handler = func(w http.ResponseWriter, r *http.Request) { - siw.Handler.PostLocalregistryEventDocker(w, r) + siw.Handler.PostEventDocker(w, r) } for _, middleware := range siw.HandlerMiddlewares { @@ -201,7 +201,7 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Get(options.BaseURL+"/api-docs", wrapper.GetApiDocs) }) r.Group(func(r chi.Router) { - r.Post(options.BaseURL+"/localregistry/event/docker", wrapper.PostLocalregistryEventDocker) + r.Post(options.BaseURL+"/event/docker", wrapper.PostEventDocker) }) r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/status", wrapper.GetStatus) @@ -213,13 +213,12 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "H4sIAAAAAAAC/5yST2vcTAzGv4rQ2e/aeXvzLTShhAQSur2FHMYz8kbUOxok2bAs/u5lvFBC+peejOB5", - "fvrJzBmjHItkym7Yn9cGOY+C/RkTWVQuzpKxx4+SPXAmhUE5HQgeC2X4fLv/AtdPd2CFIo8cwxZv0Nkn", - "+nNt/662kNpl39Wu23W4NiiFciiMPX7YdbsrbLAEf62u2IbC/yWJ23Agrx8ppBvtLmGPn8ivC9/USINK", - "ViQbbfH/u+7HIx/vcV0btPl4DHrCHh/YHGSsrgZFZeFECYYT+CuBkS4cqV4bDob9M5Z5mDjiS4W0k8Qw", - "KR3YXE8tLZS9TRK/ktbFRewnvk9i/vC2d1trN5fWP11QiXABQFDnMUQXPcHmY79yNw8+//a37i+Jv3Gy", - "OUYyG+cJvmPeWd7PA2kmJwOlkDiTGYScYOKFtqGoDASUUxHO/tZbeQlOVbwiSesbwv75jLNO2GOL68v6", - "LQAA//92GeM35gIAAA==", + "H4sIAAAAAAAC/5ySz2obQQyHX0XovLWd9ja30IQSUkioews5zM5oHdH1aJC0C8bsu5dZQynpX3IaBD99", + "+jTojEmOVQoVNwznpUMug2A4YyZLytVZCgb8KMUjF1LolfOB4KFSgS+3+69w/XgHVinxwCmu8Q6dfaR/", + "t+1ftc2kdpl3tdltdrh0KJVKrIwBP2x2myvssEZ/aa64jZXfZUlrcSBvj1TSlXaXMeAn8uvKNy3SoZJV", + "KUZr/P1u9+uSD/e4LB3adDxGPWHAz2wOMjRXg6oyc6YM/Qn8hcBIZ07Uto0Hw/CEdepHTvjcIFuaqfg2", + "S/pG2kZVsd8YPor5bUveXIJv0mwQuAAgqvMQk4ueYFWwPwmaR5/++nf7S+J/nGxKicyGaYQfmFeW91NP", + "WsjJQClmLmQGsWQYeaa1qCo9AZVchYv/7K08R6cm3pCk7VAwPJ1x0hEDbnF5Xr4HAAD//4PQ6B3LAgAA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/agent/openapi.yaml b/agent/openapi.yaml index 780e60d..b964232 100755 --- a/agent/openapi.yaml +++ b/agent/openapi.yaml @@ -28,7 +28,7 @@ paths: '200': description: OK - /localregistry/event/docker: + /event/docker: post: tags: - public diff --git a/agent/pkg/handler/docker_event_api_handler.go b/agent/pkg/handler/docker_event_api_handler.go index cbf3583..317e2e4 100644 --- a/agent/pkg/handler/docker_event_api_handler.go +++ b/agent/pkg/handler/docker_event_api_handler.go @@ -6,7 +6,7 @@ import ( "net/http" ) -func (ah *APIHandler) PostLocalregistryEventDocker(w http.ResponseWriter, r *http.Request) { +func (ah *APIHandler) PostEventDocker(w http.ResponseWriter, r *http.Request) { event, err := io.ReadAll(r.Body) if err != nil { log.Printf("Event body read failed: %v", err) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index b878a63..2897550 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 +version: 0.1.1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index d64c586..d609e0c 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 +version: 0.1.1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/client/pkg/application/application.go b/client/pkg/application/application.go index 648b37a..78b32ec 100755 --- a/client/pkg/application/application.go +++ b/client/pkg/application/application.go @@ -20,7 +20,7 @@ type Application struct { apiServer *handler.APIHandler httpServer *http.Server conn *clients.NATSContext - dbClient *clickhouse.DBClient + dbClient clickhouse.DBInterface } func New() *Application { diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 6be8b1b..4c3c3de 100755 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -16,7 +16,13 @@ type DBClient struct { conf *config.Config } -func NewDBClient(conf *config.Config) (*DBClient, error) { +type DBInterface interface { + InsertEvent(event string) + FetchEvents() []map[string]interface{} + Close() +} + +func NewDBClient(conf *config.Config) (DBInterface, error) { log.Println("Create DB if not exists") conn, err := clickhouse.Open(&clickhouse.Options{ Addr: []string{conf.DBAddress}, @@ -86,17 +92,41 @@ func NewDBClient(conf *config.Config) (*DBClient, error) { return nil, err } - return &DBClient{conn: conn}, nil + return &DBClient{conn: conn, conf: conf}, nil } -func (c *DBClient) InsertEvent(metrics string) { - log.Printf("Inserting event: %v", metrics) - insertStmt := fmt.Sprintf("INSERT INTO container_bridge FORMAT JSONAsObject %v", metrics) +func (c *DBClient) InsertEvent(event string) { + log.Printf("Inserting event: %v", event) + insertStmt := fmt.Sprintf("INSERT INTO container_bridge FORMAT JSONAsObject %v", event) if err := c.conn.Exec(context.Background(), insertStmt); err != nil { log.Printf("Insert failed, %v", err) } } +func (c *DBClient) FetchEvents() []map[string]interface{} { + log.Printf("Fetching events") + events := []map[string]interface{}{} + insertStmt := "select event from container_bridge;" + rows, err := c.conn.Query(context.Background(), insertStmt) + if err != nil { + log.Printf("Insert failed, %v", err) + } + for rows.Next() { + event := map[string]interface{}{} + if err := rows.Scan(&event); err != nil { + log.Printf("Rows scan failed: %v", err) + return events + } + fmt.Printf("row: event=%v\n", event) + events = append(events, event) + } + rows.Close() + if rows.Err() != nil { + log.Printf("Fetching rows failed: %v", rows.Err()) + } + return events +} + func (c *DBClient) Close() { _ = c.conn.Close() } diff --git a/client/pkg/clients/nats_client.go b/client/pkg/clients/nats_client.go index f4a25f2..01a9aba 100755 --- a/client/pkg/clients/nats_client.go +++ b/client/pkg/clients/nats_client.go @@ -23,10 +23,10 @@ type NATSContext struct { conf *config.Config conn *nats.Conn stream nats.JetStreamContext - dbClient *clickhouse.DBClient + dbClient clickhouse.DBInterface } -func NewNATSContext(conf *config.Config, dbClient *clickhouse.DBClient) (*NATSContext, error) { +func NewNATSContext(conf *config.Config, dbClient clickhouse.DBInterface) (*NATSContext, error) { log.Println("Waiting before connecting to NATS at:", conf.NatsAddress) time.Sleep(1 * time.Second) @@ -72,7 +72,7 @@ func (n *NATSContext) Close() { n.dbClient.Close() } -func (n *NATSContext) Subscribe(subject string, consumer string, conn *clickhouse.DBClient) { +func (n *NATSContext) Subscribe(subject string, consumer string, conn clickhouse.DBInterface) { n.stream.Subscribe(subject, func(msg *nats.Msg) { type events struct { Events []json.RawMessage `json:"events"` @@ -81,6 +81,7 @@ func (n *NATSContext) Subscribe(subject string, consumer string, conn *clickhous eventDocker := &events{} err := json.Unmarshal(msg.Data, &eventDocker) if err == nil { + log.Println(eventDocker) msg.Ack() repoName := msg.Header.Get("REPO_NAME") type newEvent struct { diff --git a/docker-compose.yaml b/docker-compose.yaml index 98d3961..f6f4e81 100755 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,15 +1,6 @@ version: '3' services: - docker-registry: - image: registry:2 - ports: - - "5001:5000" - volumes: - - ./docker-registry-config.yaml:/etc/docker/registry/config.yml - networks: - - ch_ntw - ch_server: image: clickhouse/clickhouse-server:22.6 ports: @@ -32,6 +23,7 @@ services: - "4222" ports: - "8222:8222" + - "4222:4222" networks: - ch_ntw @@ -42,45 +34,6 @@ services: networks: - ch_ntw - agent: - entrypoint: "/agent" - build: - context: ./ - dockerfile: dockerfiles/agent/Dockerfile - restart: always - environment: - NATS_TOKEN: "UfmrJOYwYCCsgQvxvcfJ3BdI6c8WBbnD" - NATS_ADDRESS: "nats://nats:4222" - PORT: "8090" - STREAM_NAME: "CONTAINERMETRICS" - ports: - - "8090:8090" - # depends_on: - # web: - # condition: service_healthy - networks: - - ch_ntw - - client: - entrypoint: "/client" - build: - context: ./ - dockerfile: dockerfiles/client/Dockerfile - restart: always - environment: - NATS_TOKEN: "UfmrJOYwYCCsgQvxvcfJ3BdI6c8WBbnD" - NATS_ADDRESS: "nats://nats:4222" - DB_ADDRESS: "ch_server:9000" - PORT: "8091" - STREAM_NAME: "CONTAINERMETRICS" - ports: - - "8091:8091" - # depends_on: - # ch_server: - # condition: service_healthy - networks: - - ch_ntw - networks: ch_ntw: driver: bridge diff --git a/docker-compose_manual_test.yaml b/docker-compose_manual_test.yaml new file mode 100755 index 0000000..b383535 --- /dev/null +++ b/docker-compose_manual_test.yaml @@ -0,0 +1,106 @@ +version: '3' + +services: + docker-registry: + image: registry:2 + ports: + - "5001:5000" + volumes: + - ./docker-registry-config.yaml:/etc/docker/registry/config.yml + networks: + - ch_ntw + + ch_server: + image: clickhouse/clickhouse-server:22.6 + ports: + - "8123:8123" + - "9000:9000" + volumes: + - ./ch_server_db:/var/lib/clickhouse + networks: + - ch_ntw + + nats: + image: nats:latest + command: + - "--jetstream" + - "--http_port" + - "8222" + environment: + NATS_HTTP_PORT_NUMBER: 8222 + expose: + - "4222" + ports: + - "8222:8222" + - "4222:4222" + networks: + - ch_ntw + + grafana: + image: grafana/grafana-enterprise:8.2.0 + ports: + - 3000:3000 + networks: + - ch_ntw + + agent: + entrypoint: "/agent" + build: + context: ./ + dockerfile: dockerfiles/agent/Dockerfile + restart: always + environment: + NATS_TOKEN: "UfmrJOYwYCCsgQvxvcfJ3BdI6c8WBbnD" + NATS_ADDRESS: "nats://nats:4222" + PORT: "8090" + STREAM_NAME: "CONTAINERMETRICS" + ports: + - "8090:8090" + deploy: + resources: + limits: + cpus: '0.50' + memory: 256M + reservations: + cpus: '0.25' + memory: 64M + # depends_on: + # web: + # condition: service_healthy + networks: + - ch_ntw + + client: + entrypoint: "/client" + build: + context: ./ + dockerfile: dockerfiles/client/Dockerfile + restart: always + environment: + NATS_TOKEN: "UfmrJOYwYCCsgQvxvcfJ3BdI6c8WBbnD" + NATS_ADDRESS: "nats://nats:4222" + DB_ADDRESS: "ch_server:9000" + PORT: "8091" + STREAM_NAME: "CONTAINERMETRICS" + ports: + - "8091:8091" + deploy: + resources: + limits: + cpus: '0.50' + memory: 256M + reservations: + cpus: '0.25' + memory: 64M + # depends_on: + # ch_server: + # condition: service_healthy + networks: + - ch_ntw + +networks: + ch_ntw: + driver: bridge + # ipam: + # config: + # - subnet: 10.222.1.0/24 diff --git a/docker-registry-config.yaml b/docker-registry-config.yaml index 05834f8..1b7a060 100755 --- a/docker-registry-config.yaml +++ b/docker-registry-config.yaml @@ -22,7 +22,7 @@ notifications: endpoints: - name: container-bridge disabled: false - url: http://agent:8090/localregistry/event + url: http://agent:8090/event/docker timeout: 10s threshold: 10 backoff: 1s diff --git a/dockerfiles/agent/Dockerfile b/dockerfiles/agent/Dockerfile index 67f5be8..0d65473 100755 --- a/dockerfiles/agent/Dockerfile +++ b/dockerfiles/agent/Dockerfile @@ -3,7 +3,7 @@ WORKDIR / COPY ./ ./ RUN go mod download -RUN go build -o ./build/agent agent/main.go +RUN CGO_ENABLED=0 go build -o ./build/agent agent/main.go FROM scratch COPY --from=builder ./build/agent agent diff --git a/dockerfiles/client/Dockerfile b/dockerfiles/client/Dockerfile index 888a56b..9cdb543 100755 --- a/dockerfiles/client/Dockerfile +++ b/dockerfiles/client/Dockerfile @@ -3,7 +3,7 @@ WORKDIR / COPY ./ ./ RUN go mod download -RUN go build -o ./build/client client/main.go +RUN CGO_ENABLED=0 go build -o ./build/client client/main.go FROM scratch COPY --from=builder ./build/client client diff --git a/go.mod b/go.mod index f952231..5104cd1 100755 --- a/go.mod +++ b/go.mod @@ -6,12 +6,13 @@ require ( github.com/ClickHouse/clickhouse-go/v2 v2.2.0 github.com/getkin/kin-openapi v0.100.0 github.com/go-chi/chi/v5 v5.0.7 - github.com/julienschmidt/httprouter v1.3.0 github.com/kelseyhightower/envconfig v1.4.0 github.com/nats-io/nats.go v1.16.0 + github.com/stretchr/testify v1.7.5 ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/swag v0.19.5 // indirect github.com/google/uuid v1.3.0 // indirect @@ -22,6 +23,7 @@ require ( github.com/nats-io/nuid v1.0.1 // indirect github.com/paulmach/orb v0.7.1 // indirect github.com/pierrec/lz4/v4 v4.1.15 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect go.opentelemetry.io/otel v1.7.0 // indirect go.opentelemetry.io/otel/trace v1.7.0 // indirect diff --git a/go.sum b/go.sum index 64a03cf..10f28d3 100755 --- a/go.sum +++ b/go.sum @@ -35,8 +35,6 @@ github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= diff --git a/integration_tests/main_test.go b/integration_tests/main_test.go new file mode 100644 index 0000000..42e75e2 --- /dev/null +++ b/integration_tests/main_test.go @@ -0,0 +1,40 @@ +package integrationtests + +import ( + "log" + "net/http" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestMain(m *testing.M) { + m.Run() +} + +func TestEvent(t *testing.T) { + data := setup() + + stop := startAagentAndClient() + + event := `{"events": [{"hash": "abcd"}]}` + // Post docker event to agent + resp, err := callHTTPRequest(http.MethodPost, "http://localhost:8090", "localregistry/event/docker", []byte(event)) + if err != nil { + t.Fail() + } + assert.Equal(t, http.StatusOK, resp.StatusCode) + + log.Println("Sleeping now") + time.Sleep(5 * time.Second) + + // Verify the event persisted in clickhouse database + events := data.dbClient.FetchEvents() + expectedEvent := `{"event":{"hash":"abcd"},"repoName":"docker registry"}` + assert.True(t, CheckExists(expectedEvent, events)) + + log.Println("Starting teardown") + tearDown(data) + stop <- true +} diff --git a/integration_tests/mock_db_client.go b/integration_tests/mock_db_client.go new file mode 100644 index 0000000..972e446 --- /dev/null +++ b/integration_tests/mock_db_client.go @@ -0,0 +1,45 @@ +package integrationtests + +import ( + "encoding/json" + "log" + "sync" + + "github.com/kube-tarian/container-bridge/client/pkg/clickhouse" + "github.com/kube-tarian/container-bridge/client/pkg/config" +) + +type MockDBClient struct { + events []map[string]interface{} + mutex sync.Mutex +} + +func NewMockDBClient(cfg *config.Config) (clickhouse.DBInterface, error) { + return &MockDBClient{ + events: []map[string]interface{}{}, + mutex: sync.Mutex{}, + }, nil +} + +func (m *MockDBClient) InsertEvent(event string) { + v := map[string]interface{}{} + json.Unmarshal([]byte(event), &v) + m.events = append(m.events, v) +} + +func (m *MockDBClient) FetchEvents() []map[string]interface{} { + return m.events +} + +func (m *MockDBClient) Close() {} + +func CheckExists(event string, events []map[string]interface{}) bool { + for _, v := range events { + actualEvent, _ := json.Marshal(v) + log.Printf("%v", string(actualEvent)) + if string(actualEvent) == event { + return true + } + } + return false +} diff --git a/integration_tests/setup_test.go b/integration_tests/setup_test.go new file mode 100644 index 0000000..4d49c5e --- /dev/null +++ b/integration_tests/setup_test.go @@ -0,0 +1,129 @@ +package integrationtests + +import ( + "bytes" + "fmt" + "log" + "net/http" + "os" + "time" + + "github.com/kelseyhightower/envconfig" + agentapp "github.com/kube-tarian/container-bridge/agent/pkg/application" + clientapp "github.com/kube-tarian/container-bridge/client/pkg/application" + "github.com/kube-tarian/container-bridge/client/pkg/clickhouse" + "github.com/kube-tarian/container-bridge/client/pkg/config" +) + +type TestContextData struct { + clientConf *config.Config + dbClient clickhouse.DBInterface +} + +func setupENV() { + os.Setenv("NATS_TOKEN", "UfmrJOYwYCCsgQvxvcfJ3BdI6c8WBbnD") + os.Setenv("NATS_ADDRESS", "nats://localhost:4222") + os.Setenv("STREAM_NAME", "CONTAINERMETRICS") + os.Setenv("DB_ADDRESS", "localhost:9000") +} + +func setup() *TestContextData { + setupENV() + cfg := &config.Config{} + if err := envconfig.Process("", cfg); err != nil { + log.Fatalf("Could not parse env Config: %v", err) + } + + // Create a db client + // dbClient, err := NewMockDBClient(cfg) + // if err != nil { + // log.Fatal(err) + // } + dbClient, err := clickhouse.NewDBClient(cfg) + if err != nil { + log.Fatal(err) + } + + return &TestContextData{ + clientConf: cfg, + dbClient: dbClient, + } +} + +func tearDown(t *TestContextData) { + // Close a db client + t.dbClient.Close() +} + +func startAagentAndClient() chan bool { + stopCh := make(chan bool) + + // Start agent and client + go startAgent(stopCh) + time.Sleep(2 * time.Second) + go startClient(stopCh) + + // Wait till Agent and Client healthy + isAgentHealthy := false + isClientHealthy := false + for { + select { + // wait till 1min, after that exit 1 + case <-time.After(1 * time.Minute): + log.Fatalf("Agent/Client not healthy") + case <-time.After(2 * time.Second): + // Check Agent health + isAgentHealthy = getHealth(http.MethodGet, "http://localhost:8090", "status", "agent") + // Check Client health + isClientHealthy = getHealth(http.MethodGet, "http://localhost:8091", "status", "client") + } + if isAgentHealthy && isClientHealthy { + break + } + } + return stopCh +} + +func getHealth(method, url, path, serviceName string) bool { + resp, err := callHTTPRequest(method, url, path, nil) + if err != nil { + log.Printf("%v health check call failed: %v", serviceName, err) + return false + } + + return checkResponse(resp, http.StatusOK) +} + +func checkResponse(resp *http.Response, statusCode int) bool { + return resp.StatusCode == statusCode +} + +func startAgent(stop chan bool) { + os.Setenv("PORT", "8090") + app := agentapp.New() + go app.Start() + + <-stop +} + +func startClient(stop chan bool) { + os.Setenv("PORT", "8091") + app := clientapp.New() + go app.Start() + <-stop +} + +func callHTTPRequest(method, url, path string, body []byte) (*http.Response, error) { + finalURL := fmt.Sprintf("%s/%s", url, path) + var req *http.Request + if body != nil { + req, _ = http.NewRequest(method, finalURL, bytes.NewBuffer(body)) + } else { + req, _ = http.NewRequest(method, finalURL, nil) + } + client := http.Client{ + Timeout: 5 * time.Second, + } + + return client.Do(req) +}