Skip to content

Commit

Permalink
Add collector-framework
Browse files Browse the repository at this point in the history
  • Loading branch information
nocturnalastro committed Feb 8, 2024
1 parent 1871893 commit 319aeb2
Show file tree
Hide file tree
Showing 96 changed files with 5,585 additions and 124 deletions.
12 changes: 12 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,15 @@ repos:
language: system
entry: bash -c 'cd tgm-collector && go mod tidy'
pass_filenames: false

- id: golangci-lint
name: golangci-lint (collector-framework) must pass
language: system
entry: bash -c 'cd collector-framework && make lint'
pass_filenames: false

- id: go-mod-tidy
name: run go mod tidy (collector-framework)
language: system
entry: bash -c 'cd collector-framework && go mod tidy'
pass_filenames: false
42 changes: 42 additions & 0 deletions collector-framework/Containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
FROM registry.access.redhat.com/ubi9 AS build

# Config
# ARGs will persist into other images, ENV vars won't. We want these to be available in both.
ENV DIR=/usr/th
ENV BIN_DIR=$DIR/bin
ENV CFG_DIR=$DIR/cfg
ENV SRC_DIR=$DIR/src

RUN dnf -y install go make

RUN mkdir ${DIR} && \
mkdir ${BIN_DIR} && \
mkdir ${CFG_DIR} && \
mkdir ${SRC_DIR}

ADD . ${SRC_DIR}
WORKDIR ${SRC_DIR}

RUN make install-tools

RUN pwd && ls
# build the test binary: outputs to ${SRC_DIR}/vse-sync-testsuite
RUN make build

RUN cp ${SRC_DIR}/vse-sync-testsuite ${BIN_DIR}/collector-tool

RUN dnf remove -y go make && \
dnf clean all && \
rm -rf ${TNF_SRC_DIR} && \
rm -rf ${TEMP_DIR} && \
rm -rf /root/.cache && \
rm -rf /root/go/pkg && \
rm -rf /root/go/src && \
rm -rf /usr/lib/golang/pkg && \
rm -rf /usr/lib/golang/src


FROM scratch
COPY --from=build / /
WORKDIR /usr/th/bin
CMD ./collector-tool
15 changes: 15 additions & 0 deletions collector-framework/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
SPDX-License-Identifier: GPL-2.0-or-later

This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
Street, Fifth Floor, Boston, MA 02110-1301, USA, search GPL-2.0-or-later or
see <https://spdx.org/licenses/GPL-2.0-or-later.html>.
27 changes: 27 additions & 0 deletions collector-framework/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.PHONY:
install-tools
build
build-image
lint

# Get default value of $GOBIN if not explicitly set
GO_PATH=$(shell go env GOPATH)
ifeq (,$(shell go env GOBIN))
GOBIN=${GO_PATH}/bin
else
GOBIN=$(shell go env GOBIN)
endif

# Install build tools and other required software.
install-tools:
go install github.com/onsi/ginkgo/v2/ginkgo

# Build test binary
build:
PATH=${PATH}:${GOBIN} go build --race

build-image:
podman build -t synctest:custom -f Containerfile

lint:
golangci-lint run --timeout 5m0s --fix
77 changes: 77 additions & 0 deletions collector-framework/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# synchronization-testsuites

The main purpose of this repo is build the necessary tooling to collect necessary synchronization-related data logs from a running OpenShift cluster. This data will then be leveraged by different analysis tools to determine if the cluster is running within acceptable bounds synchronization-wise.```

The core approach taken is to strongly encourage and enforce separation of concerns between:
1. Declarative description of the cluster(s) under test
1. Configuration of a test (e.g. number of repetitions, acceptable thresholds, etc.)
1. Collectors - methods of collecting indicative information about the cluster
1. Checks - performed on collected values

## Setup

1. [Install Go](https://go.dev/doc/install)
1. Install dependencies with `go mod tidy`

### Development Extras
1. Install dev binaries: `make install-tools`. Ensure your `$GOBIN` is on your `$PATH`
1. yamllint
1. [Install yamllint](https://yamllint.readthedocs.io/en/stable/) with `sudo yum install yamllint`
1. run with `yamllint ./`
1. golangci-lint
1. [Install golangci-lint](https://golangci-lint.run/usage/install/#local-installation)
1. run with `make lint`
1. license-eye
1. [Install license-eye](https://github.com/apache/skywalking-eyes) with `go install github.com/apache/skywalking-eyes/cmd/license-eye@latest`
1. run with `license-eye header check` or `license-eye header fix`
1. pre-commit
1. on RHEL, `pre-commit` requires recompiling python to include optional sqlite modules:
1. `sudo yum install sqlite-devel`
1. See instructions [here](https://tecadmin.net/how-to-install-python-3-10-on-centos-rhel-8-fedora/)
1. install pre-commit with `pip3.10 install pre-commit`
1. configure your repository to run pre-commit hooks with `pre-commit install`
1. manually run against all files with `pre-commit run --all-files` or against staged files with `pre-commit run`.
1. Otherwise pre-commit will now run automatically when you make a new commit.

## Usage
### Building binary
Run the following command to build the binary used in the following commands:
```shell
go build
```
###### NOTE: use the `--race` flag when developing collectors.

### Checking Enviroment
Run the following command (check help string for more details):

```shell
./collector-framework env verify --interface="<ptp interface>" --kubeconfig="${KUBECONFIG}"
```

### Running Collectors

Run the following command (check help string for more details):

```shell
./collector-framework collect --interface="<ptp interface>" --kubeconfig="${KUBECONFIG}"
```

### Fetching logs
The log subcommand has been removed. Instead we have implimented at collector which is enabled by default.
If possible you should use a log aggregator. You can control the collectors running using the `--collector` flag.

## Running tests

TODO: implement tests for all packages

To test the framework components run `ginkgo pkg/<packagename>`, for example to run the unit tests for the `config` package use `ginkgo pkg/config`

## Contributing to the repo

See [Adding a collector](doc/implementing_a_collector.md)

## To Do List

* unit tests for all of `pkg/`
* add more collectors
* better data persistance options
140 changes: 140 additions & 0 deletions collector-framework/doc/implementing_a_collector.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Implementing Collectors

## High level flow
This sequance diagram shows the high level flow of data from the SUT to User and contains
```mermaid
sequenceDiagram
participant User
participant Runner
participant Collector
participant Fetcher
participant SUT
participant callback
User ->>+ Runner: Runs binary
note left of Runner: runner.Run()
Runner ->> Runner: Gather collectors
note right of Runner: GetCollectorsToRun()
Runner ->> Collector: Starts collector
note over Runner,Collector: Runner.start() calls Collector.Start()
Runner ->> Runner: Spawns Runner.Poller() goroutine for collector
loop Runner.Poller(): Until user interupts or number of polls is reached
Runner ->>+ Collector: Calls Poll
note over Runner,Collector: Runner.Poller() calls Collector.Poll()
Collector ->>+ Fetcher: Requests Data
note over Collector,Fetcher: Collector.poll() calls function <br> in devices submodule which <br> inturn calls Fetcher.Fetch()
Fetcher ->>+ SUT: Runs commands
note left of SUT: Fetcher.Fetch() calls runCommands()
SUT ->>- Fetcher: Command result
note left of SUT: runCommands() returns result
Fetcher ->>- Collector: Returns results
note left of Fetcher: Fetcher.Fetch() unpacks results into a struct
Collector ->> callback: sends data to callback
note left of callback: Callback.Call()
callback ->> User: Presents formatted data to user
Collector ->>- Runner: Sends poll sucess/failure via results channel
note left of Collector: resultsChan <- PollResult{CollectorName, Errors}
Runner ->> Runner: Reacts to failures
end
Runner ->>- Collector: Stops collector
note left of Collector: Runner.cleanUpAll() calls Collector.CleanUp()
```

## Step by step
You will first need to create a stuct for reporting the collected values to the user. It needs to conform to the `callbacks.OutputType` interface and any fields which you wish to show the user will require a json tag.

Any collector must conform to the collector interface It should use the callback to expose collected information to the user.

Once you have filled out your collector. Any arguments should be added to the `CollectionConstuctor` and function which takes the `CollectionConstuctor` should also be defined and added to the `registry`.

An example of a very simple collector:

In `collectors/collectors.go` any arguments additional should be added to the `CollectionConstuctor`
```go
...
type CollectionConstuctor struct {
...
Msg string
}
...
```

In `collectors/announcement_collector.go` you will first need to create your reporting stuct then
you should define your collector and a constructor function which takes `CollectionConstuctor` as an argument.
```go
package collectors

import (
"fmt"

"github.com/redhat-partner-solutions/vse-sync-collection-tools/collector-framework/pkg/callbacks"
)
const (
AnnouncementCollectorName = "MyCustomerAnouncer"
AnnouncementMsg = "custom-anouncement"
)

// Reporting message
type AnnouncementMessage struct {
Msg string `json:"msg"`
}

func (annMsg *AnnouncementMessage) GetAnalyserFormat() (*callbacks.AnalyserFormatType, error) {
formatted := callbacks.AnalyserFormatType{
ID: "customAnoucner",
Data: []string{
annMsg.Msg,
},
}
return &formatted, nil
}

// Collector
type AnnouncementCollector struct {
*baseCollector
msg string
}

func (announcer *AnnouncementCollector) Poll(resultsChan chan PollResult, wg *utils.WaitGroupCount) {
defer func() {
wg.Done()
}()

msg := &AnnouncementMessage{Msg: announcer.msg}

errs := make([]error, 0)
err := announcer.callback.Call(&msg, AnnouncementMsg)
if err != nil {
errs = append(errs, fmt.Errorf("callback failed %w", err))
}
resultsChan <- PollResult{
CollectorName: AnnouncementCollectorName,
Errors: errorsToReturn,
}
}

func NewAnnouncementCollector(constuctor *CollectionConstuctor) (Collector, error) {
announcer := AnnouncementCollector{
baseCollector: newBaseCollector(
constructor.PollInterval,
false,
constructor.Callback,
),
msg:constructor.Msg,
}
return &announcer, nil
}

func init(){
// We'll make this a required collector
RegisterCollector(AnnouncementCollectorName, NewAnnouncementCollector, required)
}
```
Loading

0 comments on commit 319aeb2

Please sign in to comment.