Skip to content

Commit

Permalink
Major refurbishment
Browse files Browse the repository at this point in the history
- New config file format
- New subcommand: config view, to display configuration for current context
- New command: version, in place of a flag
- Refactored directory structure
- Completion support for bash, zsh, fish and powershell
- Add support for streaming decoding/encoding of input/output to be able to use pipes
- Structured command options ala kubectl
- Refactored template system to use go embed
  • Loading branch information
maeb committed Jun 2, 2023
1 parent 41177aa commit c1cf07c
Show file tree
Hide file tree
Showing 202 changed files with 7,235 additions and 7,047 deletions.
21 changes: 7 additions & 14 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,18 @@ jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0
-
name: Fetch all tags
run: git fetch --force --tags
-
name: Set up Go
uses: actions/setup-go@v2
- run: git fetch --force --tags
- uses: actions/setup-go@v4
with:
go-version: 1.16
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
go-version: stable
- uses: goreleaser/goreleaser-action@v4
with:
# either 'goreleaser' (default) or 'goreleaser-pro':
distribution: goreleaser
version: latest
args: release --rm-dist
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
20 changes: 5 additions & 15 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,20 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3

- uses: actions/setup-go@v2
- uses: actions/setup-go@v4
with:
go-version: '^1.16'

- name: Cache go modules
uses: actions/cache@v2
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
go-version: '^1.19'

- name: Run tests
run: go test ./...
lint:
name: Linting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: golangci/golangci-lint-action@v2
- uses: actions/checkout@v3
- uses: golangci/golangci-lint-action@v3
with:
version: latest
# Enable additional linters (see: https://golangci-lint.run/usage/linters/)
Expand Down
2 changes: 1 addition & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ archives:

release:
draft: true
prerelease: true
prerelease: "true"
28 changes: 22 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,44 @@
# Veidemannctl

[![License Apache](https://img.shields.io/github/license/nlnwa/veidemannctl.svg)](https://github.com/nlnwa/veidemannctl/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/nlnwa/veidemannctl.svg)](https://github.com/nlnwa/veidemannctl/releases/latest)

# veidemannctl
[![Go Report Card](https://goreportcard.com/badge/github.com/nlnwa/veidemannctl?style=flat-square)](https://goreportcard.com/report/github.com/nlnwa/veidemannctl)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/nlnwa/veidemannctl)](https://pkg.go.dev/github.com/nlnwa/veidemannctl)

## Install
Install the latest release version

Install the latest release version (linux/amd64)

```console
curl -sL https://raw.githubusercontent.com/nlnwa/veidemannctl/master/install.sh | bash
```

## Usage

To get a list of available commands and configuration flags:

```console
veidemanctl -h
```

## Documentation
Usage documentation: https://nlnwa.github.io/veidemannctl

## Build instructions
Usage documentation: <https://nlnwa.github.io/veidemannctl>

## Build

```console
go build
```

## Generate documentation and templates
## Test

```console
go test ./...
```

## Generate documentation

```console
go generate
```
108 changes: 81 additions & 27 deletions src/apiutil/apiutil.go → apiutil/apiutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,18 @@
package apiutil

import (
"context"
"errors"
"fmt"
"io"
"strconv"
"strings"

"github.com/nlnwa/veidemann-api/go/commons/v1"
commonsV1 "github.com/nlnwa/veidemann-api/go/commons/v1"
configV1 "github.com/nlnwa/veidemann-api/go/config/v1"
"github.com/nlnwa/veidemannctl/connection"
"github.com/nlnwa/veidemannctl/format"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
)
Expand Down Expand Up @@ -47,7 +53,7 @@ func stringSliceContains(slice []string, s string) bool {
// The filter string should have the format: <field>[.<field>]*[+|-]=<value>.
// The message should be a pointer to a proto message.
// The mask will be updated with the fields that are set.
func CreateTemplateFilter(filterString string, obj proto.Message, mask *commonsV1.FieldMask) error {
func CreateTemplateFilter(filterString string, msg proto.Message, mask *commonsV1.FieldMask) error {
path, value, ok := strings.Cut(filterString, "=")
if !ok {
return fmt.Errorf("invalid filter: %s", filterString)
Expand All @@ -59,7 +65,7 @@ func CreateTemplateFilter(filterString string, obj proto.Message, mask *commonsV
tokens := strings.Split(path, ".")

var fieldType protoreflect.FieldDescriptor
msgType := obj.ProtoReflect().Descriptor()
msgType := msg.ProtoReflect().Descriptor()

for _, token := range tokens {
fieldType = msgType.Fields().ByJSONName(token)
Expand All @@ -76,7 +82,7 @@ func CreateTemplateFilter(filterString string, obj proto.Message, mask *commonsV
if err != nil {
return fmt.Errorf("error converting %v to boolean: %w", value, err)
}
setValue(protoreflect.ValueOfBool(v), obj, fieldType)
setValue(protoreflect.ValueOfBool(v), msg, fieldType)

case protoreflect.Int32Kind:
fallthrough
Expand All @@ -87,7 +93,7 @@ func CreateTemplateFilter(filterString string, obj proto.Message, mask *commonsV
if err != nil {
return fmt.Errorf("error converting %v to int: %w", value, err)
}
setValue(protoreflect.ValueOfInt32(int32(v)), obj, fieldType)
setValue(protoreflect.ValueOfInt32(int32(v)), msg, fieldType)

case protoreflect.Int64Kind:
fallthrough
Expand All @@ -98,7 +104,7 @@ func CreateTemplateFilter(filterString string, obj proto.Message, mask *commonsV
if err != nil {
return fmt.Errorf("error converting %v to int: %w", value, err)
}
setValue(protoreflect.ValueOfInt64(v), obj, fieldType)
setValue(protoreflect.ValueOfInt64(v), msg, fieldType)

case protoreflect.Uint32Kind:
fallthrough
Expand All @@ -107,7 +113,7 @@ func CreateTemplateFilter(filterString string, obj proto.Message, mask *commonsV
if err != nil {
return fmt.Errorf("error converting %v to uint: %w", value, err)
}
setValue(protoreflect.ValueOfUint32(uint32(v)), obj, fieldType)
setValue(protoreflect.ValueOfUint32(uint32(v)), msg, fieldType)

case protoreflect.Uint64Kind:
fallthrough
Expand All @@ -116,27 +122,27 @@ func CreateTemplateFilter(filterString string, obj proto.Message, mask *commonsV
if err != nil {
return fmt.Errorf("error converting %v to uint: %w", value, err)
}
setValue(protoreflect.ValueOfUint64(v), obj, fieldType)
setValue(protoreflect.ValueOfUint64(v), msg, fieldType)

case protoreflect.FloatKind:
v, err := strconv.ParseFloat(value, 32)
if err != nil {
return fmt.Errorf("error converting %v to float: %w", value, err)
}
setValue(protoreflect.ValueOfFloat32(float32(v)), obj, fieldType)
setValue(protoreflect.ValueOfFloat32(float32(v)), msg, fieldType)

case protoreflect.DoubleKind:
v, err := strconv.ParseFloat(value, 64)
if err != nil {
return fmt.Errorf("error converting %v to float: %w", value, err)
}
setValue(protoreflect.ValueOfFloat64(v), obj, fieldType)
setValue(protoreflect.ValueOfFloat64(v), msg, fieldType)

case protoreflect.StringKind:
setValue(protoreflect.ValueOfString(value), obj, fieldType)
setValue(protoreflect.ValueOfString(value), msg, fieldType)

case protoreflect.BytesKind:
setValue(protoreflect.ValueOfBytes([]byte(value)), obj, fieldType)
setValue(protoreflect.ValueOfBytes([]byte(value)), msg, fieldType)

case protoreflect.EnumKind:
enumVal := fieldType.Enum().Values().ByName(protoreflect.Name(value))
Expand All @@ -147,17 +153,17 @@ func CreateTemplateFilter(filterString string, obj proto.Message, mask *commonsV
}
return fmt.Errorf("not a valid enum value '%s' for '%s'. Valid values: %s", value, fieldType.FullName(), strings.Join(sa, ", "))
}
setValue(protoreflect.ValueOfEnum(enumVal.Number()), obj, fieldType)
setValue(protoreflect.ValueOfEnum(enumVal.Number()), msg, fieldType)

case protoreflect.GroupKind:
case protoreflect.MessageKind:
var item protoreflect.Value
isNewFieldValue := false
if obj.ProtoReflect().Has(fieldType) {
item = obj.ProtoReflect().Mutable(fieldType)
if msg.ProtoReflect().Has(fieldType) {
item = msg.ProtoReflect().Mutable(fieldType)
} else {
isNewFieldValue = true
item = obj.ProtoReflect().NewField(fieldType)
item = msg.ProtoReflect().NewField(fieldType)
}

var message protoreflect.Message
Expand Down Expand Up @@ -187,15 +193,16 @@ func CreateTemplateFilter(filterString string, obj proto.Message, mask *commonsV
}
// Set the field value if it is a new field value
if isNewFieldValue {
obj.ProtoReflect().Set(fieldType, item)
msg.ProtoReflect().Set(fieldType, item)
}
obj = message.Interface()
msg = message.Interface()
}
}

return nil
}

// setValue sets the value of a field in a proto message.
func setValue(v protoreflect.Value, obj proto.Message, fieldType protoreflect.FieldDescriptor) {
if fieldType.IsList() {
list := obj.ProtoReflect().NewField(fieldType)
Expand All @@ -205,25 +212,72 @@ func setValue(v protoreflect.Value, obj proto.Message, fieldType protoreflect.Fi
obj.ProtoReflect().Set(fieldType, v)
}

func CreateListRequest(kind configV1.Kind, ids []string, name string, labelString string, filterString string, pageSize int32, page int32) (*configV1.ListRequest, error) {
request := &configV1.ListRequest{
// CreateListRequest creates a list request from the given parameters.
func CreateListRequest(kind configV1.Kind, ids []string, name string, labelString string, filters []string, pageSize int32, page int32) (*configV1.ListRequest, error) {
var queryMask *commonsV1.FieldMask
var queryTemplate *configV1.ConfigObject

if filters != nil {
queryMask = new(commonsV1.FieldMask)
queryTemplate = new(configV1.ConfigObject)

for _, filter := range filters {
if err := CreateTemplateFilter(filter, queryTemplate, queryMask); err != nil {
return nil, err
}
}
}

return &configV1.ListRequest{
Kind: kind,
Id: ids,
NameRegex: name,
LabelSelector: CreateSelector(labelString),
Offset: page,
PageSize: pageSize,
QueryTemplate: queryTemplate,
QueryMask: queryMask,
}, nil
}

// CompleteName returns a list of names that matches the given name.
func CompleteName(kind string, name string) ([]string, error) {
k := format.GetKind(kind)
if k == configV1.Kind_undefined {
return nil, errors.New("undefined kind")
}

request, err := CreateListRequest(k, nil, name, "", nil, 10, 0)
if err != nil {
return nil, err
}

request.ReturnedFieldsMask = &commons.FieldMask{
Paths: []string{"meta.name"},
}

conn, err := connection.Connect()
if err != nil {
return nil, err
}
defer conn.Close()

if filterString != "" {
queryMask := new(commonsV1.FieldMask)
queryTemplate := new(configV1.ConfigObject)
if err := CreateTemplateFilter(filterString, queryTemplate, queryMask); err != nil {
client := configV1.NewConfigClient(conn)
r, err := client.ListConfigObjects(context.Background(), request)
if err != nil {
return nil, err
}

var names []string
for {
msg, err := r.Recv()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return nil, err
}
request.QueryMask = queryMask
request.QueryTemplate = queryTemplate
names = append(names, msg.Meta.Name)
}

return request, nil
return names, nil
}
11 changes: 6 additions & 5 deletions src/apiutil/apiutil_test.go → apiutil/apiutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@

package apiutil

import (
import (
"reflect"
"testing"

commonsV1 "github.com/nlnwa/veidemann-api/go/commons/v1"
configV1 "github.com/nlnwa/veidemann-api/go/config/v1"
frontierV1 "github.com/nlnwa/veidemann-api/go/frontier/v1"
"google.golang.org/protobuf/proto"
"reflect"
"testing"

api "github.com/nlnwa/veidemann-api/go/config/v1"
)
Expand Down Expand Up @@ -85,7 +86,7 @@ func TestCreateListRequest(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := CreateListRequest(api.Kind_browserConfig, tt.args.ids, tt.args.name, tt.args.labelString, "", tt.args.pageSize, tt.args.page)
got, err := CreateListRequest(api.Kind_browserConfig, tt.args.ids, tt.args.name, tt.args.labelString, nil, tt.args.pageSize, tt.args.page)
if err != nil {
t.Errorf("Error in CreateListRequest(): %v", err)
}
Expand Down Expand Up @@ -135,7 +136,7 @@ func TestCreateTemplateFilter(t *testing.T) {
{"oneof/int",
args{filterString: "browserConfig.pageLoadTimeoutMs=100", templateObj: &configV1.ConfigObject{}},
&commonsV1.FieldMask{Paths: []string{"browserConfig.pageLoadTimeoutMs"}},
&configV1.ConfigObject{Spec: &configV1.ConfigObject_BrowserConfig{&configV1.BrowserConfig{PageLoadTimeoutMs: 100}}},
&configV1.ConfigObject{Spec: &configV1.ConfigObject_BrowserConfig{BrowserConfig: &configV1.BrowserConfig{PageLoadTimeoutMs: 100}}},
false,
},
{"illegal filter for template",
Expand Down
Loading

0 comments on commit c1cf07c

Please sign in to comment.