Skip to content
This repository has been archived by the owner on Oct 10, 2023. It is now read-only.

Commit

Permalink
Support test plugins with context-aware-cli-for-plugins feature-flag …
Browse files Browse the repository at this point in the history
…enabled

Signed-off-by: Anuj Chaudhari <anujc@vmware.com>
  • Loading branch information
anujc25 committed Sep 22, 2022
1 parent ed3014a commit aed7a58
Show file tree
Hide file tree
Showing 18 changed files with 165 additions and 47 deletions.
20 changes: 9 additions & 11 deletions .github/workflows/plugin_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,29 +82,27 @@ jobs:
echo "##[set-output name=bompath;]$(echo "$BOMCOMMENT" | awk -F : '{print $2}')"
id: extract_bom

# Using `ENABLE_CONTEXT_AWARE_PLUGIN_DISCOVERY=false` to build and install cli and plugins because
# currently, context-aware-plugin-discovery does not support test plugins
# Issue: https://github.com/vmware-tanzu/tanzu-framework/issues/1164
# above issue need to be addressed and as part of the change we can use context-aware way of plugin
# tests as part of this test workflow.
- name: Build
run: |
if [[ ! -z "${{ steps.extract_bom.outputs.bompath }}" ]]; then
export TKG_DEFAULT_COMPATIBILITY_IMAGE_PATH=${{ steps.extract_bom.outputs.bompath }}
fi
env | sort
make configure-bom
make build-cli-local ENABLE_CONTEXT_AWARE_PLUGIN_DISCOVERY=false
make build-cli-local
- name: Cleanup
run: rm -rf ~/.tanzu ~/.tkg ~/.config ~/.cache/tanzu; docker kill $(docker ps -q) | true; docker system prune -a --volumes -f

- name: Install Tanzu CLI
run: |
make install-cli ENABLE_CONTEXT_AWARE_PLUGIN_DISCOVERY=false
make install-cli
tanzu config set features.global.context-aware-cli-for-plugins true
- name: Install CLI plugins
run: make install-cli-plugins ENABLE_CONTEXT_AWARE_PLUGIN_DISCOVERY=false

- name: Cleanup
run: rm -rf ~/.tanzu ~/.tkg ~/.config ~/.cache/tanzu; docker kill $(docker ps -q) | true; docker system prune -a --volumes -f
run: |
tanzu plugin install all --local artifacts/linux/amd64/cli/
tanzu plugin install all --local artifacts-admin/linux/amd64/cli/
- name: Run cluster test plugin
run: tanzu test plugin cluster
7 changes: 7 additions & 0 deletions cli/core/pkg/artifact/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"io"
"net/http"
"time"

"github.com/pkg/errors"
)

const (
Expand Down Expand Up @@ -72,3 +74,8 @@ func (g *HTTPArtifact) Fetch() ([]byte, error) {

return out, nil
}

// FetchTest returns test artifact
func (g *HTTPArtifact) FetchTest() ([]byte, error) {
return nil, errors.New("fetching test plugin from HTTP source is not yet supported")
}
2 changes: 2 additions & 0 deletions cli/core/pkg/artifact/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
type Artifact interface {
// Fetch the binary for a plugin version.
Fetch() ([]byte, error)
// FetchTest the test binary for a plugin version.
FetchTest() ([]byte, error)
}

// NewURIArtifact creates new artifacts based on the URI
Expand Down
29 changes: 29 additions & 0 deletions cli/core/pkg/artifact/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,32 @@ func (l *LocalArtifact) Fetch() ([]byte, error) {
}
return b, nil
}

// FetchTest reads test plugin artifact based on the local plugin artifact path
// To fetch the test binary from the plugin we are using plugin binary path and creating test plugin path from it
// If the plugin binary path is `artifacts/darwin/amd64/cli/cluster/v0.27.0-dev/tanzu-cluster-darwin_amd64`
// then test plugin binary will be under `artifacts/darwin/amd64/cli/cluster/v0.27.0-dev/test/` directory
func (l *LocalArtifact) FetchTest() ([]byte, error) {
// test plugin directory based on the plugin binary location
testPluginDir := filepath.Join(filepath.Dir(l.Path), "test")
dirEntries, err := os.ReadDir(testPluginDir)
if err != nil {
return nil, errors.Wrap(err, "unable to read test plugin directory")
}
if len(dirEntries) != 1 {
return nil, errors.Errorf("Expected only 1 file under the '%v' directory, but found %v files", testPluginDir, len(dirEntries))
}

// Assuming the file under the test directory is the test plugin binary
testPluginFile := dirEntries[0]
if testPluginFile.IsDir() {
return nil, errors.Errorf("Expected to find test plugin binary but found directory '%v'", testPluginFile)
}

testPluginPath := filepath.Join(testPluginDir, testPluginFile.Name())
b, err := os.ReadFile(testPluginPath)
if err != nil {
return nil, errors.Wrapf(err, "error while reading test artifact")
}
return b, nil
}
48 changes: 48 additions & 0 deletions cli/core/pkg/artifact/local_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2021 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package artifact

import (
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
)

// When local artifact directory exists and test directory exists
func TestLocalArtifactWhenArtifactAndTestDirExists(t *testing.T) {
assert := assert.New(t)

testPluginPath, err := filepath.Abs(filepath.Join("test", "local", "v0.0.1", "tanzu-plugin-darwin_amd64"))
assert.NoError(err)
artifact := NewLocalArtifact(testPluginPath)

b, err := artifact.Fetch()
assert.NoError(err)
assert.Contains(string(b), "plugin binary")

b, err = artifact.FetchTest()
assert.NoError(err)
assert.Contains(string(b), "test plugin binary")
}

// When local artifact doesn't exists and multiple files exists within test directory
func TestLocalArtifactWhenArtifactDoesntExistsAndMultipleFilesUnderTest(t *testing.T) {
assert := assert.New(t)

testPluginPath, err := filepath.Abs(filepath.Join("test", "local", "v0.0.2", "plugin_binary_does_not_exists"))
assert.NoError(err)

artifact := NewLocalArtifact(testPluginPath)

// When local artifact doesn't exists
_, err = artifact.Fetch()
assert.Error(err)
assert.ErrorContains(err, "error while reading artifact")

// When multiple files exists under test directory
_, err = artifact.FetchTest()
assert.Error(err)
assert.ErrorContains(err, "Expected only 1 file under the")
}
5 changes: 5 additions & 0 deletions cli/core/pkg/artifact/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,8 @@ func (g *OCIArtifact) Fetch() ([]byte, error) {

return bytesData, nil
}

// FetchTest returns test artifact
func (g *OCIArtifact) FetchTest() ([]byte, error) {
return nil, errors.New("fetching test plugin from OCI source is not yet supported")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
plugin binary
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test plugin binary
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
plugin binary
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test plugin binary
4 changes: 2 additions & 2 deletions cli/core/pkg/cli/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ func TestCmd(p *cliapi.PluginDescriptor) *cobra.Command {
Use: p.Name,
Short: p.Description,
RunE: func(cmd *cobra.Command, args []string) error {
runner := NewRunner(p.Name, p.InstallationPath, args)
runner := NewRunner(p.Name, p.TestPluginInstallationPath, args)
ctx := context.Background()
return runner.RunTest(ctx)
return runner.Run(ctx)
},
DisableFlagParsing: true,
}
Expand Down
21 changes: 21 additions & 0 deletions cli/core/pkg/distribution/artifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,27 @@ func (aMap Artifacts) Fetch(version, os, arch string) ([]byte, error) {
"arch:%s", version, os, arch)
}

// FetchTest the test binary for a plugin version.
func (aMap Artifacts) FetchTest(version, os, arch string) ([]byte, error) {
a, err := aMap.GetArtifact(version, os, arch)
if err != nil {
return nil, err
}

if a.Image != "" {
return artifact.NewOCIArtifact(a.Image).FetchTest()
}
if a.URI != "" {
u, err := artifact.NewURIArtifact(a.URI)
if err != nil {
return nil, err
}
return u.FetchTest()
}

return nil, errors.Errorf("invalid artifact for version:%s, os:%s, arch:%s", version, os, arch)
}

// GetDigest returns the SHA256 hash of the binary for a plugin version.
func (aMap Artifacts) GetDigest(version, os, arch string) (string, error) {
a, err := aMap.GetArtifact(version, os, arch)
Expand Down
3 changes: 3 additions & 0 deletions cli/core/pkg/distribution/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ type Distribution interface {
// Fetch the binary for a plugin version.
Fetch(version, os, arch string) ([]byte, error)

// FetchTest the test binary for a plugin version.
FetchTest(version, os, arch string) ([]byte, error)

// GetDigest returns the SHA256 hash of the binary for a plugin version.
GetDigest(version, os, arch string) (string, error)

Expand Down
10 changes: 10 additions & 0 deletions cli/core/pkg/pluginmanager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,16 @@ func installOrUpgradePlugin(serverName string, p *plugin.Discovered, version str
descriptor.Discovery = p.Source
descriptor.DiscoveredRecommendedVersion = p.RecommendedVersion

b, err = p.Distribution.FetchTest(version, runtime.GOOS, runtime.GOARCH)
if err == nil {
testpluginPath := filepath.Join(common.DefaultPluginRoot, pluginName, fmt.Sprintf("test-%s", version))
err = os.WriteFile(testpluginPath, b, 0755)
if err != nil {
return errors.Wrap(err, "error while saving test plugin binary")
}
descriptor.TestPluginInstallationPath = testpluginPath
}

c, err := catalog.NewContextCatalog(serverName)
if err != nil {
return err
Expand Down
4 changes: 4 additions & 0 deletions cli/runtime/apis/cli/v1alpha1/catalog_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ type PluginDescriptor struct {
// E.g., cluster/v0.3.2@sha256:...
InstallationPath string `json:"installationPath"`

// TestPluginInstallationPath is a installation path for a test plugin binary.
// E.g., cluster/test-v0.3.2
TestPluginInstallationPath string `json:"testPluginInstallationPath"`

// Discovery is the name of the discovery from where
// this plugin is discovered.
Discovery string `json:"discovery"`
Expand Down
1 change: 1 addition & 0 deletions cmd/cli/plugin-admin/test/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,7 @@ github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mo
github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
github.com/otiai10/copy v1.4.2 h1:RTiz2sol3eoXPLF4o+YWqEybwfUa/Q2Nkc4ZIUs3fwI=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
Expand Down
54 changes: 20 additions & 34 deletions cmd/cli/plugin-admin/test/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
package main

import (
"path/filepath"
"errors"

"github.com/aunum/log"
"github.com/spf13/cobra"

"github.com/vmware-tanzu/tanzu-framework/cli/core/pkg/cli"
"github.com/vmware-tanzu/tanzu-framework/cli/core/pkg/pluginmanager"
cliapi "github.com/vmware-tanzu/tanzu-framework/cli/runtime/apis/cli/v1alpha1"
"github.com/vmware-tanzu/tanzu-framework/cli/runtime/buildinfo"
"github.com/vmware-tanzu/tanzu-framework/cli/runtime/config"
"github.com/vmware-tanzu/tanzu-framework/cli/runtime/plugin"
)

Expand All @@ -24,66 +24,52 @@ var descriptor = cliapi.PluginDescriptor{
BuildSHA: buildinfo.SHA,
}

var local []string
var local string

func init() {
fetchCmd.PersistentFlags().StringSliceVarP(&local, "local", "l", []string{}, "paths to local repository")
fetchCmd.PersistentFlags().StringVarP(&local, "local", "l", "", "paths to local repository")
}

func main() {
p, err := plugin.NewPlugin(&descriptor)
if err != nil {
log.Fatal(err)
}

p.AddCommands(
fetchCmd,
pluginsCmd,
)
descs, err := cli.ListPlugins("test")

_, standalonePlugins, err := pluginmanager.InstalledPlugins("")
if err != nil {
log.Fatal(err)
}

for _, d := range descs {
pluginsCmd.AddCommand(cli.TestCmd(d))
for _, d := range standalonePlugins {
if d.TestPluginInstallationPath == "" {
continue
}
pluginsCmd.AddCommand(cli.TestCmd(d.DeepCopy()))
}

if err := p.Execute(); err != nil {
log.Fatal(err)
}
}

var fetchCmd = &cobra.Command{
Use: "fetch",
Short: "Fetch the plugin tests",
RunE: func(cmd *cobra.Command, args []string) error {
repos := getRepositories()
err := cli.EnsureTests(repos, "test")
if err != nil {
log.Fatal(err)
}
return nil
},
}

var pluginsCmd = &cobra.Command{
Use: "plugin",
Short: "Plugin tests",
}

func getRepositories() *cli.MultiRepo {
if len(local) != 0 {
m := cli.NewMultiRepo()
for _, l := range local {
n := filepath.Base(l)
r := cli.NewLocalRepository(n, l)
m.AddRepository(r)
var fetchCmd = &cobra.Command{
Use: "fetch",
Short: "Fetch the plugin tests",
RunE: func(cmd *cobra.Command, args []string) error {
if local == "" {
return errors.New("please specify paths to local repository containing test plugins with `--local` flag ")
}
return m
}
cfg, err := config.GetClientConfig()
if err != nil {
log.Fatal(err)
}
return cli.NewMultiRepo(cli.LoadRepositories(cfg)...)
return pluginmanager.InstallPluginsFromLocalSource("all", "", local)
},
}

0 comments on commit aed7a58

Please sign in to comment.