Compare commits

..

2 Commits

Author SHA1 Message Date
RoyUP9
30a85a4b92 Added tap pre check (#848) 2022-02-24 11:48:42 +02:00
Adam Kol
cdbacff996 Cypress: Service Map test (#847) 2022-02-23 17:36:31 +02:00
7 changed files with 197 additions and 13 deletions

View File

@@ -64,6 +64,8 @@ it('right side sanity test', function () {
}); });
}); });
serviceMapCheck();
checkIllegalFilter('invalid filter'); checkIllegalFilter('invalid filter');
checkFilter({ checkFilter({
@@ -188,7 +190,7 @@ function checkFilter(filterDetails){
const entriesForDeeperCheck = 5; const entriesForDeeperCheck = 5;
it(`checking the filter: ${name}`, function () { it(`checking the filter: ${name}`, function () {
cy.get('#total-entries').then(number => { cy.get('#total-entries').should('not.have.text', '0').then(number => {
const totalEntries = number.text(); const totalEntries = number.text();
// checks the hover on the last entry (the only one in DOM at the beginning) // checks the hover on the last entry (the only one in DOM at the beginning)
@@ -320,3 +322,42 @@ function checkOnlyLineNumberes(jsonItems, decodedText) {
cy.get(`${Cypress.env('bodyJsonClass')} >`).should('have.length', 1).and('have.text', decodedText); cy.get(`${Cypress.env('bodyJsonClass')} >`).should('have.length', 1).and('have.text', decodedText);
cy.get(`${Cypress.env('bodyJsonClass')} > >`).should('have.length', jsonItems) cy.get(`${Cypress.env('bodyJsonClass')} > >`).should('have.length', jsonItems)
} }
function serviceMapCheck() {
it('service map test', function () {
cy.intercept(`${Cypress.env('testUrl')}/servicemap/get`).as('serviceMapRequest');
cy.get('#total-entries').should('not.have.text', '0').then(() => {
cy.get('#total-entries').invoke('text').then(entriesNum => {
cy.get('[alt="service-map"]').click();
cy.wait('@serviceMapRequest').then(({response}) => {
const body = response.body;
const nodeParams = {
destination: 'httpbin.mizu-tests',
source: '127.0.0.1'
};
serviceMapAPICheck(body, parseInt(entriesNum), nodeParams);
cy.reload();
});
});
});
});
}
function serviceMapAPICheck(body, entriesNum, nodeParams) {
const {nodes, edges} = body;
expect(nodes.length).to.equal(Object.keys(nodeParams).length, `Expected nodes count`);
expect(edges.some(edge => edge.source.name === nodeParams.source)).to.be.true;
expect(edges.some(edge => edge.destination.name === nodeParams.destination)).to.be.true;
let count = 0;
edges.forEach(edge => {
count += edge.count;
if (edge.destination.name === nodeParams.destination) {
expect(edge.source.name).to.equal(nodeParams.source);
}
});
expect(count).to.equal(entriesNum);
}

View File

@@ -1,8 +1,11 @@
package cmd package cmd
import ( import (
"github.com/creasty/defaults"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/up9inc/mizu/cli/config/configStructs"
"github.com/up9inc/mizu/cli/telemetry" "github.com/up9inc/mizu/cli/telemetry"
"github.com/up9inc/mizu/shared/logger"
) )
var checkCmd = &cobra.Command{ var checkCmd = &cobra.Command{
@@ -17,4 +20,11 @@ var checkCmd = &cobra.Command{
func init() { func init() {
rootCmd.AddCommand(checkCmd) rootCmd.AddCommand(checkCmd)
defaultCheckConfig := configStructs.CheckConfig{}
if err := defaults.Set(&defaultCheckConfig); err != nil {
logger.Log.Debug(err)
}
checkCmd.Flags().Bool(configStructs.PreTapCheckName, defaultCheckConfig.PreTap, "Check pre-tap Mizu installation for potential problems")
} }

View File

@@ -3,6 +3,10 @@ package cmd
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/up9inc/mizu/shared"
rbac "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/scheme"
"regexp" "regexp"
"github.com/up9inc/mizu/cli/apiserver" "github.com/up9inc/mizu/cli/apiserver"
@@ -14,7 +18,7 @@ import (
) )
func runMizuCheck() { func runMizuCheck() {
logger.Log.Infof("Mizu install checks\n===================") logger.Log.Infof("Mizu checks\n===================")
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() // cancel will be called when this function exits defer cancel() // cancel will be called when this function exits
@@ -25,17 +29,23 @@ func runMizuCheck() {
checkPassed = checkKubernetesVersion(kubernetesVersion) checkPassed = checkKubernetesVersion(kubernetesVersion)
} }
var isInstallCommand bool if config.Config.Check.PreTap {
if checkPassed { if checkPassed {
checkPassed, isInstallCommand = checkMizuMode(ctx, kubernetesProvider) checkPassed = checkK8sTapPermissions(ctx, kubernetesProvider)
} }
} else {
var isInstallCommand bool
if checkPassed {
checkPassed, isInstallCommand = checkMizuMode(ctx, kubernetesProvider)
}
if checkPassed { if checkPassed {
checkPassed = checkK8sResources(ctx, kubernetesProvider, isInstallCommand) checkPassed = checkK8sResources(ctx, kubernetesProvider, isInstallCommand)
} }
if checkPassed { if checkPassed {
checkPassed = checkServerConnection(kubernetesProvider) checkPassed = checkServerConnection(kubernetesProvider)
}
} }
if checkPassed { if checkPassed {
@@ -273,9 +283,81 @@ func checkResourceExist(resourceName string, resourceType string, exist bool, er
} else if !exist { } else if !exist {
logger.Log.Errorf("%v '%v' %v doesn't exist", fmt.Sprintf(uiUtils.Red, "✗"), resourceName, resourceType) logger.Log.Errorf("%v '%v' %v doesn't exist", fmt.Sprintf(uiUtils.Red, "✗"), resourceName, resourceType)
return false return false
} else {
logger.Log.Infof("%v '%v' %v exists", fmt.Sprintf(uiUtils.Green, "√"), resourceName, resourceType)
} }
logger.Log.Infof("%v '%v' %v exists", fmt.Sprintf(uiUtils.Green, "√"), resourceName, resourceType)
return true
}
func checkK8sTapPermissions(ctx context.Context, kubernetesProvider *kubernetes.Provider) bool {
logger.Log.Infof("\nkubernetes-permissions\n--------------------")
var filePath string
if config.Config.IsNsRestrictedMode() {
filePath = "./examples/roles/permissions-ns-tap.yaml"
} else {
filePath = "./examples/roles/permissions-all-namespaces-tap.yaml"
}
data, err := shared.ReadFromFile(filePath)
if err != nil {
logger.Log.Errorf("%v error while checking kubernetes permissions, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
return false
}
obj, err := getDecodedObject(data)
if err != nil {
logger.Log.Errorf("%v error while checking kubernetes permissions, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
return false
}
var rules []rbac.PolicyRule
if config.Config.IsNsRestrictedMode() {
rules = obj.(*rbac.Role).Rules
} else {
rules = obj.(*rbac.ClusterRole).Rules
}
return checkPermissions(ctx, kubernetesProvider, rules)
}
func getDecodedObject(data []byte) (runtime.Object, error) {
decode := scheme.Codecs.UniversalDeserializer().Decode
obj, _, err := decode(data, nil, nil)
if err != nil {
return nil, err
}
return obj, nil
}
func checkPermissions(ctx context.Context, kubernetesProvider *kubernetes.Provider, rules []rbac.PolicyRule) bool {
permissionsExist := true
for _, rule := range rules {
for _, group := range rule.APIGroups {
for _, resource := range rule.Resources {
for _, verb := range rule.Verbs {
exist, err := kubernetesProvider.CanI(ctx, config.Config.MizuResourcesNamespace, resource, verb, group)
permissionsExist = checkPermissionExist(group, resource, verb, exist, err) && permissionsExist
}
}
}
}
return permissionsExist
}
func checkPermissionExist(group string, resource string, verb string, exist bool, err error) bool {
if err != nil {
logger.Log.Errorf("%v error checking permission for %v %v in group '%v', err: %v", fmt.Sprintf(uiUtils.Red, "✗"), verb, resource, group, err)
return false
} else if !exist {
logger.Log.Errorf("%v can't %v %v in group '%v'", fmt.Sprintf(uiUtils.Red, "✗"), verb, resource, group)
return false
}
logger.Log.Infof("%v can %v %v in group '%v'", fmt.Sprintf(uiUtils.Green, "√"), verb, resource, group)
return true return true
} }

View File

@@ -22,6 +22,7 @@ const (
type ConfigStruct struct { type ConfigStruct struct {
Tap configStructs.TapConfig `yaml:"tap"` Tap configStructs.TapConfig `yaml:"tap"`
Check configStructs.CheckConfig `yaml:"check"`
Version configStructs.VersionConfig `yaml:"version"` Version configStructs.VersionConfig `yaml:"version"`
View configStructs.ViewConfig `yaml:"view"` View configStructs.ViewConfig `yaml:"view"`
Logs configStructs.LogsConfig `yaml:"logs"` Logs configStructs.LogsConfig `yaml:"logs"`

View File

@@ -0,0 +1,9 @@
package configStructs
const (
PreTapCheckName = "pre-tap"
)
type CheckConfig struct {
PreTap bool `yaml:"pre-tap"`
}

20
shared/fileUtils.go Normal file
View File

@@ -0,0 +1,20 @@
package shared
import (
"io/ioutil"
"os"
)
func ReadFromFile(path string) ([]byte, error) {
reader, err := os.Open(path)
if err != nil {
return nil, err
}
data, err := ioutil.ReadAll(reader)
if err != nil {
return nil, err
}
return data, nil
}

View File

@@ -17,6 +17,7 @@ import (
"github.com/up9inc/mizu/shared/semver" "github.com/up9inc/mizu/shared/semver"
"github.com/up9inc/mizu/tap/api" "github.com/up9inc/mizu/tap/api"
v1 "k8s.io/api/apps/v1" v1 "k8s.io/api/apps/v1"
auth "k8s.io/api/authorization/v1"
core "k8s.io/api/core/v1" core "k8s.io/api/core/v1"
rbac "k8s.io/api/rbac/v1" rbac "k8s.io/api/rbac/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors" k8serrors "k8s.io/apimachinery/pkg/api/errors"
@@ -443,6 +444,26 @@ func (provider *Provider) CreateService(ctx context.Context, namespace string, s
return provider.clientSet.CoreV1().Services(namespace).Create(ctx, &service, metav1.CreateOptions{}) return provider.clientSet.CoreV1().Services(namespace).Create(ctx, &service, metav1.CreateOptions{})
} }
func (provider *Provider) CanI(ctx context.Context, namespace string, resource string, verb string, group string) (bool, error) {
selfSubjectAccessReview := &auth.SelfSubjectAccessReview{
Spec: auth.SelfSubjectAccessReviewSpec{
ResourceAttributes: &auth.ResourceAttributes{
Namespace: namespace,
Resource: resource,
Verb: verb,
Group: group,
},
},
}
response, err := provider.clientSet.AuthorizationV1().SelfSubjectAccessReviews().Create(ctx, selfSubjectAccessReview, metav1.CreateOptions{})
if err != nil {
return false, err
}
return response.Status.Allowed, nil
}
func (provider *Provider) DoesNamespaceExist(ctx context.Context, name string) (bool, error) { func (provider *Provider) DoesNamespaceExist(ctx context.Context, name string) (bool, error) {
namespaceResource, err := provider.clientSet.CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{}) namespaceResource, err := provider.clientSet.CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{})
return provider.doesResourceExist(namespaceResource, err) return provider.doesResourceExist(namespaceResource, err)