Skip to content

Commit

Permalink
Quote the cluster-name (#12)
Browse files Browse the repository at this point in the history
* Quote the cluster-name

Signed-off-by: Dominique Vernier <[email protected]>

* miss the point ;-) in the cluster name

Signed-off-by: Dominique Vernier <[email protected]>

* managedClusterName => managedCluster.name

Signed-off-by: Dominique Vernier <[email protected]>

* mv GetClientFromFlags->GetControllerRuntimeClient.

Signed-off-by: Dominique Vernier <[email protected]>

* Split resources and tests

Signed-off-by: Dominique Vernier <[email protected]>

* Improve unit-test

Signed-off-by: Dominique Vernier <[email protected]>

* Add extra unit-tests

Signed-off-by: Dominique Vernier <[email protected]>

* Fix readme

Signed-off-by: Dominique Vernier <[email protected]>
  • Loading branch information
itdove authored Mar 29, 2021
1 parent f703fb2 commit 03393a2
Show file tree
Hide file tree
Showing 77 changed files with 535 additions and 168 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

# Test binary, built with `go test -c`
*.test
tmp*
test/unit/coverage
test/unit/tmp
test/functional/tmp
Expand Down
54 changes: 53 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,56 @@ order to be assigned an issue or pull request, you must be a member of the
[open-cluster-management](https://github.com/open-cluster-management) GitHub organization.

Repo maintainers can assign you an issue or pull request by leaving a
`/assign <your Github ID>` comment on the issue or pull request.
`/assign <your Github ID>` comment on the issue or pull request.

# Requirements

- Go 1.16

# Develop new commands

- The project tries to follow the following grammar for the commands:

```bash
cm <verb> <noun>
```

- A number of verbs are already defined in [verbs](pkg/cmd/verbs/verbs.go), if you would like to add a new verb or noun, please contact the [OWNERS](OWNERS).

- The noun represents the object on which the verb applies.

- Each pair (verb/noum) has its own package. For example [create/cluster](pkg/cmd/delete/cluster/cmd.go) package contains the code to create a cluster.

- Inside the package, the code is split in 3 files: The [cmd.go](pkg/cmd/create/cluster/cmd.go) which creates the cobra command, the [options.go](pkg/cmd/create/cluster/options.go) which defines the different option parameters for the command and the the [exec.go](pkg/cmd/create/cluster/exec.go) which contains the code to execute the command.


## Resources

- Some commands needs resources files, in the project uses the `Go 1.16` `go:embed` functionality to store the resources files.
- Each command package contains its own resources in the scenario package. The scenario package contains one go file which provides the `go:embed` `embed.FS` files. For example [resources.go](pkg/cmd/create/cluster/scenario/resources.go).
- All resources must be accessed using unstrusctured, the project must not have api dependencies.

## Applier

- This project relies on the [applier](https://github.com/open-cluster-management/applier) to create, update or delete kubernetes resources. The applier allows you to create templated resources and these templates are parsed with a provided values files and then applied in the kubernetes cluster. For example, the [scenario](pkg/cmd/create/cluster/scenario) contains the templated resources to create a managed cluster on a hub.

## Client

- The [helpers](pkg/helpers/client.go) package contains methods to get a client. For the time being only a `sigs.k8s.io/controller-runtime/pkg/client` is used as it is the one needed for the applier, but if you would like to use another client for other goals, please add the method to create client in that package. The most important to to get the config from:

```Go
config, err := configFlags.ToRESTConfig()
```

as it uses also the parameters like `--server` or `--kubeconfig` to generate the client.

## Unit tests

- If the unit test needs files to be executed, these files are stored under the pair `<verb>/<noun>/test/unit` like [values-fake/yaml](pkg/cmd/detach/cluster/test/unit/values-fake.yaml).
A total coverage is shown when running `make test`. For the time being, the `cmd.go` and `client.go` are excluded from the total coverage.
- The `make test` is part of the PR acceptance and it is launched by PROW.

## Functional tests

- The project runs functional-tests `make functional-test-full`, this test deploys a [KiND](https://kind.sigs.k8s.io/) cluster, install some resource using the applier and then runs a set of tests against that cluster [run-functional-tests.sh](build/run-functional-tests.sh).
- The `make functional-tests-full` is part of the PR acceptance and it is launched using git-actions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ See our [Contributing Document](CONTRIBUTING.md) for more information.
The commands are composed of a verb and a noum and then a number of parameters.

## Cluster commands

[applier](docs/applier.md)

[cluster](docs/cluster.md)

3 changes: 1 addition & 2 deletions build/run-unit-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@

_script_dir=$(dirname "$0")
mkdir -p test/unit/coverage
rm -rf test/unit/tmp
mkdir test/unit/tmp

echo 'mode: atomic' > test/unit/coverage/cover.out
echo '' > test/unit/coverage/cover.tmp
echo -e "${GOPACKAGES// /\\n}" | xargs -n1 -I{} $_script_dir/test-package.sh {} ${GOPACKAGES// /,}
Expand Down
9 changes: 6 additions & 3 deletions build/test-package.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@ mkdir -p $_tap_out_dir

# Run tests
# DO NOT USE -coverpkg=./...
go test -v -cover -coverpkg=$_cover_pkgs -covermode=atomic -coverprofile=test/unit/coverage/cover.tmp $_package 2> >( grep -v "warning: no packages being tested depend on" >&2 ) | $GOPATH/bin/patter | tee $_tap_out_dir/$_tap_name.tap | grep -v "TAP version 13" | grep -v ": PASS:" | grep -v -i "# /us"
go test -v -cover -coverpkg=$_cover_pkgs -covermode=atomic -coverprofile=test/unit/coverage/cover.out.tmp $_package 2> >( grep -v "warning: no packages being tested depend on" >&2 ) | $GOPATH/bin/patter | tee $_tap_out_dir/$_tap_name.tap | grep -v "TAP version 13" | grep -v ": PASS:" | grep -v -i "# /us"

# Merge coverage files
if [ -f test/unit/coverage/cover.tmp ]; then
if [ -f test/unit/coverage/cover.out.tmp ]; then
# Filtering
cat test/unit/coverage/cover.out.tmp | grep -v "cmd.go" | grep -v "client.go" > test/unit/coverage/cover.tmp
$GOPATH/bin/gocovmerge test/unit/coverage/cover.tmp test/unit/coverage/cover.out > test/unit/coverage/cover.all
mv test/unit/coverage/cover.all test/unit/coverage/cover.out
rm -f test/unit/coverage/cover.tmp
fi

# Clean up temporary files
rm -f test/unit/coverage/cover.tmp
rm -f test/unit/coverage/cover.out.tmp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright Contributors to the Open Cluster Management project

package resources
package applierscenarios

import (
"embed"
Expand All @@ -9,26 +9,35 @@ import (
"path/filepath"
"strings"

"github.com/open-cluster-management/applier/pkg/templateprocessor"

"github.com/ghodss/yaml"
)

type Resources struct{}
type ApplierScenarioReader interface {
templateprocessor.TemplateReader
ExtractAssets(prefix, dir string) error
}

type ApplierScenarioResourcesReader struct {
files *embed.FS
}

//Needed to scenarios/*/*/*/* to include the _helpers.tpl located in:
//scenarios/create/hub/common/_helpers.tpl and
//scenarios/destroy/hub/common/_helpers.tpl
//go:embed scenarios scenarios/*/*/*/_helpers.tpl
var files embed.FS
var _ ApplierScenarioReader = &ApplierScenarioResourcesReader{
files: nil,
}

func NewResourcesReader() *Resources {
return &Resources{}
func NewApplierScenarioResourcesReader(files *embed.FS) *ApplierScenarioResourcesReader {
return &ApplierScenarioResourcesReader{
files: files,
}
}

func (*Resources) Asset(name string) ([]byte, error) {
return files.ReadFile(name)
func (r *ApplierScenarioResourcesReader) Asset(name string) ([]byte, error) {
return r.files.ReadFile(name)
}

func (b *Resources) AssetNames() ([]string, error) {
func (b *ApplierScenarioResourcesReader) AssetNames() ([]string, error) {
assetNames := make([]string, 0)
got, err := b.assetWalk(".")
if err != nil {
Expand All @@ -37,9 +46,9 @@ func (b *Resources) AssetNames() ([]string, error) {
return append(assetNames, got...), nil
}

func (b *Resources) assetWalk(f string) ([]string, error) {
func (r *ApplierScenarioResourcesReader) assetWalk(f string) ([]string, error) {
assets := make([]string, 0)
file, err := files.Open(f)
file, err := r.files.Open(f)
if err != nil {
return assets, err
}
Expand All @@ -48,7 +57,7 @@ func (b *Resources) assetWalk(f string) ([]string, error) {
return assets, err
}
if fs.IsDir() {
de, err := files.ReadDir(f)
de, err := r.files.ReadDir(f)
if err != nil {
return assets, err
}
Expand All @@ -57,7 +66,7 @@ func (b *Resources) assetWalk(f string) ([]string, error) {
if err != nil {
return assets, nil
}
assetsDir, err := b.assetWalk(filepath.Join(f, di.Name()))
assetsDir, err := r.assetWalk(filepath.Join(f, di.Name()))
if err != nil {
return assets, err
}
Expand All @@ -68,11 +77,11 @@ func (b *Resources) assetWalk(f string) ([]string, error) {
return append(assets, f), nil
}

func (*Resources) ToJSON(b []byte) ([]byte, error) {
func (r *ApplierScenarioResourcesReader) ToJSON(b []byte) ([]byte, error) {
return yaml.YAMLToJSON(b)
}

func (r *Resources) ExtractAssets(prefix, dir string) error {
func (r *ApplierScenarioResourcesReader) ExtractAssets(prefix, dir string) error {
assetNames, err := r.AssetNames()
if err != nil {
return err
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Copyright Contributors to the Open Cluster Management project

package resources
package applierscenarios

import (
"embed"
"fmt"
"io/ioutil"
"os"
Expand All @@ -11,11 +12,13 @@ import (
"testing"
)

//go:embed embed_test
var files embed.FS
var testDir = filepath.Join("..", "..", "test", "unit")
var testDirTmp = filepath.Join(testDir, "tmp")

func TestResources_Asset(t *testing.T) {
asset := "scenarios/attach/hub/managed_cluster_cr.yaml"
asset := "embed_test/detach/hub/managed_cluster_cr.yaml"
basset, errFile := ioutil.ReadFile(asset)
if errFile != nil {
t.Error(errFile)
Expand All @@ -25,23 +28,23 @@ func TestResources_Asset(t *testing.T) {
}
tests := []struct {
name string
b *Resources
b *ApplierScenarioResourcesReader
args args
want []byte
wantErr bool
}{
{
name: "Existing asset",
b: &Resources{},
b: NewApplierScenarioResourcesReader(&files),
args: args{
name: "scenarios/attach/hub/managed_cluster_cr.yaml",
name: "embed_test/detach/hub/managed_cluster_cr.yaml",
},
want: basset,
wantErr: false,
},
{
name: "Not found asset",
b: &Resources{},
b: NewApplierScenarioResourcesReader(&files),
args: args{
name: "hello",
},
Expand All @@ -66,18 +69,18 @@ func TestResources_Asset(t *testing.T) {
func TestResources_AssetNames(t *testing.T) {
tests := []struct {
name string
b *Resources
b *ApplierScenarioResourcesReader
wantErr bool
}{
{
name: "Existing asset",
b: &Resources{},
b: NewApplierScenarioResourcesReader(&files),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b := &Resources{}
b := NewApplierScenarioResourcesReader(&files)
got, err := b.AssetNames()
if (err != nil) != tt.wantErr {
t.Errorf("Resources.AssetNames() error = %v, wantErr %v", err, tt.wantErr)
Expand All @@ -91,14 +94,12 @@ func TestResources_AssetNames(t *testing.T) {
}
}
//Check if all files in resources are in AssetNames except resources.go and resources_test.go
err = filepath.Walk(".",
err = filepath.Walk("./embed_test",
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() &&
path != "resources.go" &&
path != "resources_test.go" {
if !info.IsDir() {
// t.Logf("Check file: %s", path)
found := false
for _, a := range got {
Expand Down Expand Up @@ -127,14 +128,14 @@ func TestResources_ToJSON(t *testing.T) {
}
tests := []struct {
name string
b *Resources
b *ApplierScenarioResourcesReader
args args
want []byte
wantErr bool
}{
{
name: "Good yaml",
b: &Resources{},
b: NewApplierScenarioResourcesReader(&files),
args: args{
b: []byte("greetings: hello"),
},
Expand All @@ -143,7 +144,7 @@ func TestResources_ToJSON(t *testing.T) {
},
{
name: "Bad yaml",
b: &Resources{},
b: &ApplierScenarioResourcesReader{},
args: args{
b: []byte(": hello"),
},
Expand All @@ -153,7 +154,7 @@ func TestResources_ToJSON(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b := &Resources{}
b := NewApplierScenarioResourcesReader(&files)
got, err := b.ToJSON(tt.args.b)
if (err != nil) != tt.wantErr {
t.Errorf("Resources.ToJSON() error = %v, wantErr %v", err, tt.wantErr)
Expand All @@ -169,16 +170,18 @@ func TestResources_ToJSON(t *testing.T) {
func TestNewResourcesReader(t *testing.T) {
tests := []struct {
name string
want *Resources
want *ApplierScenarioResourcesReader
}{
{
name: "Create",
want: &Resources{},
want: &ApplierScenarioResourcesReader{
files: nil,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewResourcesReader(); !reflect.DeepEqual(got, tt.want) {
if got := NewApplierScenarioResourcesReader(nil); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewResourcesReader() = %v, want %v", got, tt.want)
}
})
Expand All @@ -192,30 +195,30 @@ func TestResources_ExtractAssets(t *testing.T) {
}
tests := []struct {
name string
r *Resources
r *ApplierScenarioResourcesReader
args args
wantErr bool
}{
{
name: "Existing prefix",
args: args{
prefix: "scenarios/attach/hub",
prefix: "embed_test/detach/hub",
dir: filepath.Join(testDirTmp, "exist_prefix"),
},
wantErr: false,
},
{
name: "Existing name",
args: args{
prefix: "scenarios/attach/hub/managed_cluster_cr.yaml",
prefix: "embed_test/detach/hub/managed_cluster_cr.yaml",
dir: filepath.Join(testDirTmp, "exist_name"),
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Resources{}
r := NewApplierScenarioResourcesReader(&files)
os.RemoveAll(tt.args.dir)
if err := r.ExtractAssets(tt.args.prefix, tt.args.dir); (err != nil) != tt.wantErr {
t.Errorf("Resources.ExtractAssets() error = %v, wantErr %v", err, tt.wantErr)
Expand Down
Loading

0 comments on commit 03393a2

Please sign in to comment.