Skip to content

Commit

Permalink
New run Command with Test DSL, Graceful Shutdown, HTTP Server and Pro…
Browse files Browse the repository at this point in the history
…metheus Metrics, and Sender Support (hyperledger#26)

* first attempt at new run command

Signed-off-by: hfuss <[email protected]>

* command working

Signed-off-by: hfuss <[email protected]>

* run multiple instances with nohup tested

Signed-off-by: hfuss <[email protected]>

* fixing instance names in prep.sh

Signed-off-by: hfuss <[email protected]>

* docker image and k8s dashboard

Signed-off-by: hfuss <[email protected]>

* somehow missed an import

Signed-off-by: hfuss <[email protected]>

* Daemon Mode (#1)

* Daemon Mode

Signed-off-by: hfuss <[email protected]>

* finished daemon server; testing now

Signed-off-by: hfuss <[email protected]>

* fixed graceful shutdown; fixed /status

Signed-off-by: hfuss <[email protected]>

* more robust stack support

Signed-off-by: hfuss <[email protected]>

* ensuring docker builds are k8s compatible

Signed-off-by: hfuss <[email protected]>

* cleaing up code, shut down enhancements, always running server for metrics scraping

Signed-off-by: hfuss <[email protected]>

* forgot to commit new server package

Signed-off-by: hfuss <[email protected]>

* file header

Signed-off-by: hfuss <[email protected]>

* removing unused isdaemon method

Signed-off-by: hfuss <[email protected]>

* combining deliquent action w/ graceful shutdown

Signed-off-by: hfuss <[email protected]>

* Requiring Sender DID (#2)

* Requiring Sender DID

Signed-off-by: hfuss <[email protected]>

* tested / debugged sender support

Signed-off-by: hfuss <[email protected]>

* histogram for perf test duration

Signed-off-by: hfuss <[email protected]>

* fix

Signed-off-by: hfuss <[email protected]>

* recipient and recipientaddress are optional

Signed-off-by: hfuss <[email protected]>

* registering prom metrics

Signed-off-by: hfuss <[email protected]>

* simplifying perf runner shutdown with cancelling ctx

Signed-off-by: hfuss <[email protected]>

* timing out in main loop in the event all workers stop

Signed-off-by: hfuss <[email protected]>

* private messages only receive two confirmations in a multi-member network (multi-receiver not supported by perf tests yet)

Signed-off-by: hfuss <[email protected]>

* spacing

Signed-off-by: hfuss <[email protected]>

* Starting subscriptions on connect

Signed-off-by: hfuss <[email protected]>

* more logging

Signed-off-by: hfuss <[email protected]>

* fixing missed merge conflict

Signed-off-by: hfuss <[email protected]>

* PR feedback

Signed-off-by: hfuss <[email protected]>

* refactoring test names to use enums

Signed-off-by: hfuss <[email protected]>

* sender support for prep.sh

Signed-off-by: hfuss <[email protected]>

* including ldflags in make install; ensuring getLogs gets all logs

Signed-off-by: hfuss <[email protected]>

* updating examples and outputting instances.yaml

Signed-off-by: hfuss <[email protected]>

* need to close wsconns on graceful shutdown to avoid heartbeat noise during shutdown

Signed-off-by: hfuss <[email protected]>

* Long run config

Signed-off-by: Peter Broadhurst <[email protected]>

* Typo

Signed-off-by: Peter Broadhurst <[email protected]>

* Add check for unknown test and fix prep.sh

Signed-off-by: Peter Broadhurst <[email protected]>

* 5m -> 500h

Signed-off-by: Peter Broadhurst <[email protected]>

* Add recipient

Signed-off-by: Peter Broadhurst <[email protected]>

* simplifying the sender / recipient inputs

Signed-off-by: hfuss <[email protected]>

* fixing stack parsing

Signed-off-by: hfuss <[email protected]>

* updating chart example values

Signed-off-by: hfuss <[email protected]>

Co-authored-by: Peter Broadhurst <[email protected]>
  • Loading branch information
onelapahead and peterbroadhurst authored Apr 22, 2022
1 parent bba8ce6 commit b7857b1
Show file tree
Hide file tree
Showing 28 changed files with 2,818 additions and 298 deletions.
16 changes: 16 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.github/
hack/
.gitignore
.goreleaser.yml
CODEOWNERS
main
.DS_Store
ffperf/ffperf
ffperf.log
.vscode
dist/
*.iml
.idea/
*.md
grafanaDashboard.json
config/
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
ARG BUILD_VERSION=canary

FROM golang:1.17-alpine3.15 as builder

RUN apk add make gcc build-base curl git
WORKDIR /firefly-perf-cli
ADD go.mod go.sum ./
RUN go mod download
ADD . .
RUN make VERSION=$BUILD_VERSION build

FROM alpine:3.15 as final
WORKDIR /firefly-perf-cli
COPY --from=builder /firefly-perf-cli/ffperf/ffperf ./ffperf
RUN ln -s /firefly-perf-cli/ffperf /usr/bin/ffperf
ENTRYPOINT ["ffperf", "run"]
CMD ["--help"]
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ VGO=go
GOBIN := $(shell $(VGO) env GOPATH)/bin
GITREF := $(shell git rev-parse --short HEAD)
DATE := $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
VERSION := canary
LINT := $(GOBIN)/golangci-lint

all: build
build: ## Builds all go code
cd ffperf && go build -ldflags="-X 'github.com/hyperledger/firefly-perf-cli/internal/version.Date=$(DATE)' -X 'github.com/hyperledger/firefly-perf-cli/internal/version.Commit=$(GITREF)'"
cd ffperf && go build -ldflags="-X 'github.com/hyperledger/firefly-perf-cli/internal/version.Version=$(VERSION)' -X 'github.com/hyperledger/firefly-perf-cli/internal/version.Date=$(DATE)' -X 'github.com/hyperledger/firefly-perf-cli/internal/version.Commit=$(GITREF)'"
install: ## Installs the package
cd ffperf && go install
cd ffperf && go install -ldflags="-X 'github.com/hyperledger/firefly-perf-cli/internal/version.Version=$(VERSION)' -X 'github.com/hyperledger/firefly-perf-cli/internal/version.Date=$(DATE)' -X 'github.com/hyperledger/firefly-perf-cli/internal/version.Commit=$(GITREF)'"

docker:
docker build --platform linux/amd64 --build-arg BUILD_VERSION=$(VERSION) . -t ghcr.io/hyperledger/firefly-perf-cli

lint: ${LINT} ## Checks and reports lint errors
GOGC=20 $(LINT) run -v --timeout 5m
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ FireFly Performance CLI is a HTTP load testing tool that generates a constant re
- [x] Private Messaging (`POST /messages/private`)
- [x] Mint Tokens (`POST /tokens/mint`)
- [x] Fungible vs. Non-Fungible Token Toggle
- [ ] Blobs
- [x] Blobs

## Run a test

Expand Down
8 changes: 5 additions & 3 deletions charts/ffperf/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ stack: ""
# fireflyHostname: "firefly.org0-firefly.svc.cluster.local"
# useHttps: false
# exposedFireflyPort: 3000
# address: 0xddddddddd
# - id: "1"
# orgName: "org1"
# nodeName: "org1-firefly"
# fireflyHostname: "firefly.org1-firefly.svc.cluster.local"
# useHttps: false
# exposedFireflyPort: 3000
# address: 0xaaaaaaaaa

wsConfig:
wsPath: /ws
Expand All @@ -41,10 +43,10 @@ wsConfig:

instances: []
# - name: ff0-ff1-broadcast
# test: msg_broadcast
# tests: ["msg_broadcast"]
# length: 5m
# recipient: did:firefly:org/org_1
# recipientAddress: 0x912362b183362c8349d57a61e1c0c9183c08cff1
# sender: 0
# recipient: 1
# workers: 10
# messageOptions:
# longMessage: true
Expand Down
29 changes: 15 additions & 14 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
/*
Copyright © 2021 NAME HERE <EMAIL ADDRESS>
// Copyright © 2022 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd

import (
Expand Down
204 changes: 204 additions & 0 deletions cmd/run.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
// Copyright © 2022 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
"encoding/json"
"fmt"
"io/ioutil"
"path"

"github.com/hyperledger/firefly-perf-cli/internal/conf"
"github.com/hyperledger/firefly-perf-cli/internal/perf"
"github.com/hyperledger/firefly-perf-cli/internal/server"
"github.com/hyperledger/firefly-perf-cli/internal/types"
"github.com/hyperledger/firefly/pkg/fftypes"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
)

var configFilePath string
var instanceName string
var instanceIndex int
var daemonOverride bool
var deliquentAction string

var httpServer *server.HttpServer
var perfRunner perf.PerfRunner

// runCmd represents the run command
var runCmd = &cobra.Command{
Use: "run",
Short: "Executes a instance within a performance test suite to generate synthetic load across multiple FireFly nodes within a network",
Long: GetFireflyAsciiArt() + `
Executes a instance within a performance test suite to generate synthetic load across multiple FireFly nodes within a network
`,
PreRunE: func(cmd *cobra.Command, args []string) error {
config, err := loadConfig(configFilePath)
if err != nil {
return err
}

if !config.Daemon {
config.Daemon = daemonOverride
}

if instanceName != "" && instanceIndex != -1 {
log.Warn("both the \"instance-name\" and \"instance-index\" flags were provided, using \"instance-name\"")
}

instance, err := selectInstance(config)
if err != nil {
return err
}

runnerConfig, err := generateRunnerConfigFromInstance(instance, config)
if err != nil {
return err
}

perfRunner = perf.New(runnerConfig)
httpServer = server.NewHttpServer()

return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
err := perfRunner.Init()
if err != nil {
return err
}

go httpServer.Run()
return perfRunner.Start()
},
}

func init() {
rootCmd.AddCommand(runCmd)

runCmd.Flags().StringVarP(&configFilePath, "config", "c", "", "Path to performance config that describes the network and test instances")
runCmd.Flags().StringVarP(&instanceName, "instance-name", "n", "", "Instance within performance config to run against the network")
runCmd.Flags().IntVarP(&instanceIndex, "instance-idx", "i", -1, "Index of the instance within performance config to run against the network")
runCmd.Flags().BoolVarP(&daemonOverride, "daemon", "d", false, "Run in long-lived, daemon mode. Any provided test length is ignored.")
runCmd.Flags().StringVarP(&deliquentAction, "delinquent", "", "exit", "Action to take when delinquent messages are detected. Valid options: [exit log]")

runCmd.MarkFlagRequired("config")
}

func loadConfig(filename string) (*conf.PerformanceTestConfig, error) {
if d, err := ioutil.ReadFile(filename); err != nil {
return nil, err
} else {
var config *conf.PerformanceTestConfig
var err error
if path.Ext(filename) == ".yaml" || path.Ext(filename) == ".yml" {
err = yaml.Unmarshal(d, &config)
} else {
err = json.Unmarshal(d, &config)
}

if err != nil {
return nil, err
}
return config, nil
}
}

func selectInstance(config *conf.PerformanceTestConfig) (*conf.InstanceConfig, error) {
if instanceName != "" {
for _, i := range config.Instances {
if i.Name == instanceName {
return &i, nil
}
}
return nil, errors.Errorf("did not find instance named \"%s\" within the provided config", instanceName)
} else if instanceIndex != -1 {
if instanceIndex >= len(config.Instances) || instanceIndex < 0 {
return nil, errors.Errorf("provided instance index \"%d\" is outside of the range of instances within the provided config", instanceIndex)
}
return &config.Instances[instanceIndex], nil
}

return nil, errors.Errorf("please set either the \"instance-name\" or \"instance-index\" ")
}

func generateRunnerConfigFromInstance(instance *conf.InstanceConfig, perfConfig *conf.PerformanceTestConfig) (*conf.PerfRunnerConfig, error) {
runnerConfig := &conf.PerfRunnerConfig{
Tests: instance.Tests,
}

runnerConfig.StackJSONPath = perfConfig.StackJSONPath
stack, stackErr := readStackJSON(runnerConfig.StackJSONPath)
if stackErr != nil {
return nil, stackErr
}
runnerConfig.NodeURLs = make([]string, len(stack.Members))
for i, member := range stack.Members {
if member.FireflyHostname == "" {
member.FireflyHostname = "localhost"
}
scheme := "http"
if member.UseHTTPS {
scheme = "https"
}

runnerConfig.NodeURLs[i] = fmt.Sprintf("%s://%s:%v", scheme, member.FireflyHostname, member.ExposedFireflyPort)
}

runnerConfig.MessageOptions = instance.MessageOptions
runnerConfig.TokenOptions = instance.TokenOptions
runnerConfig.ContractOptions = instance.ContractOptions
runnerConfig.Workers = instance.Workers
runnerConfig.Length = instance.Length
runnerConfig.WebSocket = perfConfig.WSConfig
runnerConfig.Daemon = perfConfig.Daemon
runnerConfig.DelinquentAction = deliquentAction

runnerConfig.SenderURL = runnerConfig.NodeURLs[instance.Sender]
if instance.Recipient != nil {
runnerConfig.RecipientOrg = fmt.Sprintf("did:firefly:org/%s", stack.Members[*instance.Recipient].OrgName)
runnerConfig.RecipientAddress = stack.Members[*instance.Recipient].Address
}

err := validateConfig(*runnerConfig)
if err != nil {
return nil, err
}

return runnerConfig, nil
}

func validateConfig(cfg conf.PerfRunnerConfig) error {
if cfg.TokenOptions.TokenType != "" && cfg.TokenOptions.TokenType != fftypes.TokenTypeFungible.String() && cfg.TokenOptions.TokenType != fftypes.TokenTypeNonFungible.String() {
return fmt.Errorf("invalid token type. Choose from [%s %s]", fftypes.TokenTypeFungible.String(), fftypes.TokenTypeNonFungible.String())
}
return nil
}

func readStackJSON(filename string) (*types.Stack, error) {
if d, err := ioutil.ReadFile(filename); err != nil {
return nil, err
} else {
var stack *types.Stack
if err := json.Unmarshal(d, &stack); err != nil {
return nil, err
}
return stack, nil
}
}
Loading

0 comments on commit b7857b1

Please sign in to comment.