Compare commits

...

6 Commits

Author SHA1 Message Date
Igor Gov
d34dacbbe2 View command - moving version check after proxy creation (#177) 2021-08-08 12:26:58 +03:00
Nimrod Gilboa Markevich
0595df8b87 Adds Namespace-Restricted Mode to README. (#178) 2021-08-08 12:23:11 +03:00
Nimrod Gilboa Markevich
ebbe6458a8 Do not tap pods whose names start with "mizu-". (#176) 2021-08-08 10:51:39 +03:00
Igor Gov
7f2021c312 Several fixes for the release (#175) 2021-08-08 10:32:21 +03:00
RoyUP9
824945141a fixed config parsing of int and uint (#172) 2021-08-05 21:45:18 +03:00
Igor Gov
0244f12167 Fixes (#171) 2021-08-05 19:29:06 +03:00
26 changed files with 449 additions and 280 deletions

View File

@@ -14,9 +14,9 @@ A simple-yet-powerful API traffic viewer for Kubernetes to help you troubleshoot
## Download
Download `mizu` for your platform and operating system
Download Mizu for your platform and operating system
### Latest stable release
### Latest Stable Release
* for MacOS - Intel
```
@@ -34,12 +34,12 @@ https://github.com/up9inc/mizu/releases/latest/download/mizu_linux_amd64 \
SHA256 checksums are available on the [Releases](https://github.com/up9inc/mizu/releases) page.
### Development (unstable) build
### Development (unstable) Build
Pick one from the [Releases](https://github.com/up9inc/mizu/releases) page.
## Prerequisites
1. Set `KUBECONFIG` environment variable to your kubernetes configuration. If this is not set, mizu assumes that configuration is at `${HOME}/.kube/config`
2. mizu needs following permissions on your kubernetes cluster to run
1. Set `KUBECONFIG` environment variable to your Kubernetes configuration. If this is not set, Mizu assumes that configuration is at `${HOME}/.kube/config`
2. Mizu needs following permissions on your Kubernetes cluster to run
```yaml
- apiGroups:
@@ -84,7 +84,7 @@ Pick one from the [Releases](https://github.com/up9inc/mizu/releases) page.
- get
```
3. Optionally, for resolving traffic IP to kubernetes service name, mizu needs below permissions
3. Optionally, for resolving traffic IP to Kubernetes service name, Mizu needs below permissions
```yaml
- apiGroups:
@@ -201,7 +201,7 @@ Pick one from the [Releases](https://github.com/up9inc/mizu/releases) page.
- watch
```
4. Optionally, in order to use the policy rules validation feature, mizu requires the following additional permissions:
4. Optionally, in order to use the policy rules validation feature, Mizu requires the following additional permissions:
```yaml
- apiGroups:
@@ -214,7 +214,7 @@ Pick one from the [Releases](https://github.com/up9inc/mizu/releases) page.
- delete
```
5. Alternatively, in order to restrict mizu to one namespace only (by setting `agent.namespace` in the config file), mizu needs the following permissions in that namespace:
5. Alternatively, in order to restrict Mizu to one namespace only (by setting `agent.namespace` in the config file), Mizu needs the following permissions in that namespace:
```yaml
- apiGroups:
@@ -252,7 +252,7 @@ Pick one from the [Releases](https://github.com/up9inc/mizu/releases) page.
- get
```
6. To restrict mizu to one namespace while also resolving IPs, mizu needs the following permissions in that namespace:
6. To restrict Mizu to one namespace while also resolving IPs, Mizu needs the following permissions in that namespace:
```yaml
- apiGroups:
@@ -346,7 +346,7 @@ Pick one from the [Releases](https://github.com/up9inc/mizu/releases) page.
See `examples/roles` for example `clusterroles`.
## How to run
## How to Run
1. Find pods you'd like to tap to in your Kubernetes cluster
2. Run `mizu tap PODNAME` or `mizu tap REGEX`
@@ -388,3 +388,17 @@ To tap multiple pods using regex -
^C
```
## Advanced Usage
### Namespace-Restricted Mode
Some users have permission to only manage resources in one particular namespace assigned to them.
By default `mizu tap` creates a new namespace `mizu` for all of its Kubernetes resources. In order to instead install
Mizu in an existing namespace, set the `mizu-resources-namespace` config option.
If `mizu-resources-namespace` is set to a value other than the default `mizu`, Mizu will operate in a
Namespace-Restricted mode. It will only tap pods in `mizu-resources-namespace`. This way Mizu only requires permissions
to the namespace set by `mizu-resources-namespace`. The user must set the tapped namespace to the same namespace by
using the `--namespace` flag or by setting `tap.namespaces` in the config file.
Setting `mizu-resources-namespace=mizu` resets Mizu to its default behavior.

View File

@@ -13,6 +13,7 @@ require (
github.com/go-playground/validator/v10 v10.5.0
github.com/google/martian v2.1.0+incompatible
github.com/gorilla/websocket v1.4.2
github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/romana/rlog v0.0.0-20171115192701-f018bc92e7d7
github.com/up9inc/mizu/shared v0.0.0

View File

@@ -28,13 +28,13 @@ var k8sResolver *resolver.Resolver
func StartResolving(namespace string) {
errOut := make(chan error, 100)
res, err := resolver.NewFromInCluster(errOut)
res, err := resolver.NewFromInCluster(errOut, namespace)
if err != nil {
rlog.Infof("error creating k8s resolver %s", err)
return
}
ctx := context.Background()
res.Start(ctx, namespace)
res.Start(ctx)
go func() {
for {
select {

View File

@@ -1,6 +1,7 @@
package resolver
import (
cmap "github.com/orcaman/concurrent-map"
"k8s.io/client-go/kubernetes"
_ "k8s.io/client-go/plugin/pkg/client/auth/azure"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
@@ -9,7 +10,7 @@ import (
restclient "k8s.io/client-go/rest"
)
func NewFromInCluster(errOut chan error) (*Resolver, error) {
func NewFromInCluster(errOut chan error, namesapce string) (*Resolver, error) {
config, err := restclient.InClusterConfig()
if err != nil {
return nil, err
@@ -18,5 +19,5 @@ func NewFromInCluster(errOut chan error) (*Resolver, error) {
if err != nil {
return nil, err
}
return &Resolver{clientConfig: config, clientSet: clientset, nameMap: make(map[string]string), serviceMap: make(map[string]string), errOut: errOut}, nil
return &Resolver{clientConfig: config, clientSet: clientset, nameMap: cmap.New(), serviceMap: cmap.New(), errOut: errOut, namespace: namesapce}, nil
}

View File

@@ -7,12 +7,14 @@ import (
"github.com/romana/rlog"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
"github.com/orcaman/concurrent-map"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
)
const (
kubClientNullString = "None"
)
@@ -20,17 +22,16 @@ const (
type Resolver struct {
clientConfig *restclient.Config
clientSet *kubernetes.Clientset
nameMap map[string]string
serviceMap map[string]string
nameMap cmap.ConcurrentMap
serviceMap cmap.ConcurrentMap
isStarted bool
errOut chan error
namespace string
}
func (resolver *Resolver) Start(ctx context.Context, namespace string) {
func (resolver *Resolver) Start(ctx context.Context) {
if !resolver.isStarted {
resolver.isStarted = true
resolver.namespace = namespace
go resolver.infiniteErrorHandleRetryFunc(ctx, resolver.watchServices)
go resolver.infiniteErrorHandleRetryFunc(ctx, resolver.watchEndpoints)
@@ -39,19 +40,19 @@ func (resolver *Resolver) Start(ctx context.Context, namespace string) {
}
func (resolver *Resolver) Resolve(name string) string {
resolvedName, isFound := resolver.nameMap[name]
resolvedName, isFound := resolver.nameMap.Get(name)
if !isFound {
return ""
}
return resolvedName
return resolvedName.(string)
}
func (resolver *Resolver) GetMap() map[string]string {
func (resolver *Resolver) GetMap() cmap.ConcurrentMap {
return resolver.nameMap
}
func (resolver *Resolver) CheckIsServiceIP(address string) bool {
_, isFound := resolver.serviceMap[address]
_, isFound := resolver.serviceMap.Get(address)
return isFound
}
@@ -63,17 +64,17 @@ func (resolver *Resolver) watchPods(ctx context.Context) error {
}
for {
select {
case event := <- watcher.ResultChan():
if event.Object == nil {
return errors.New("error in kubectl pod watch")
}
if event.Type == watch.Deleted {
pod := event.Object.(*corev1.Pod)
resolver.saveResolvedName(pod.Status.PodIP, "", event.Type)
}
case <- ctx.Done():
watcher.Stop()
return nil
case event := <-watcher.ResultChan():
if event.Object == nil {
return errors.New("error in kubectl pod watch")
}
if event.Type == watch.Deleted {
pod := event.Object.(*corev1.Pod)
resolver.saveResolvedName(pod.Status.PodIP, "", event.Type)
}
case <-ctx.Done():
watcher.Stop()
return nil
}
}
}
@@ -86,37 +87,37 @@ func (resolver *Resolver) watchEndpoints(ctx context.Context) error {
}
for {
select {
case event := <- watcher.ResultChan():
if event.Object == nil {
return errors.New("error in kubectl endpoint watch")
}
endpoint := event.Object.(*corev1.Endpoints)
serviceHostname := fmt.Sprintf("%s.%s", endpoint.Name, endpoint.Namespace)
if endpoint.Subsets != nil {
for _, subset := range endpoint.Subsets {
var ports []int32
if subset.Ports != nil {
for _, portMapping := range subset.Ports {
if portMapping.Port > 0 {
ports = append(ports, portMapping.Port)
}
case event := <-watcher.ResultChan():
if event.Object == nil {
return errors.New("error in kubectl endpoint watch")
}
endpoint := event.Object.(*corev1.Endpoints)
serviceHostname := fmt.Sprintf("%s.%s", endpoint.Name, endpoint.Namespace)
if endpoint.Subsets != nil {
for _, subset := range endpoint.Subsets {
var ports []int32
if subset.Ports != nil {
for _, portMapping := range subset.Ports {
if portMapping.Port > 0 {
ports = append(ports, portMapping.Port)
}
}
if subset.Addresses != nil {
for _, address := range subset.Addresses {
resolver.saveResolvedName(address.IP, serviceHostname, event.Type)
for _, port := range ports {
ipWithPort := fmt.Sprintf("%s:%d", address.IP, port)
resolver.saveResolvedName(ipWithPort, serviceHostname, event.Type)
}
}
}
}
if subset.Addresses != nil {
for _, address := range subset.Addresses {
resolver.saveResolvedName(address.IP, serviceHostname, event.Type)
for _, port := range ports {
ipWithPort := fmt.Sprintf("%s:%d", address.IP, port)
resolver.saveResolvedName(ipWithPort, serviceHostname, event.Type)
}
}
}
}
case <- ctx.Done():
watcher.Stop()
return nil
}
case <-ctx.Done():
watcher.Stop()
return nil
}
}
}
@@ -129,7 +130,7 @@ func (resolver *Resolver) watchServices(ctx context.Context) error {
}
for {
select {
case event := <- watcher.ResultChan():
case event := <-watcher.ResultChan():
if event.Object == nil {
return errors.New("error in kubectl service watch")
}
@@ -145,7 +146,7 @@ func (resolver *Resolver) watchServices(ctx context.Context) error {
resolver.saveResolvedName(ingress.IP, serviceHostname, event.Type)
}
}
case <- ctx.Done():
case <-ctx.Done():
watcher.Stop()
return nil
}
@@ -154,19 +155,19 @@ func (resolver *Resolver) watchServices(ctx context.Context) error {
func (resolver *Resolver) saveResolvedName(key string, resolved string, eventType watch.EventType) {
if eventType == watch.Deleted {
delete(resolver.nameMap, key)
resolver.nameMap.Remove(key)
rlog.Infof("setting %s=nil\n", key)
} else {
resolver.nameMap[key] = resolved
resolver.nameMap.Set(key, resolved)
rlog.Infof("setting %s=%s\n", key, resolved)
}
}
func (resolver *Resolver) saveServiceIP(key string, resolved string, eventType watch.EventType) {
if eventType == watch.Deleted {
delete(resolver.serviceMap, key)
resolver.serviceMap.Remove(key)
} else {
resolver.serviceMap[key] = resolved
resolver.serviceMap.Set(key, resolved)
}
}
@@ -189,4 +190,3 @@ func (resolver *Resolver) infiniteErrorHandleRetryFunc(ctx context.Context, fun
}
}
}

View File

@@ -3,8 +3,8 @@ package cmd
import (
"context"
"github.com/spf13/cobra"
"github.com/up9inc/mizu/cli/fsUtils"
"github.com/up9inc/mizu/cli/kubernetes"
"github.com/up9inc/mizu/cli/logsUtils"
"github.com/up9inc/mizu/cli/mizu"
"os"
"path"
@@ -32,7 +32,7 @@ var logsCmd = &cobra.Command{
}
mizu.Log.Debugf("Using file path %s", filePath)
if err := logsUtils.DumpLogs(kubernetesProvider, ctx, filePath); err != nil {
if err := fsUtils.DumpLogs(kubernetesProvider, ctx, filePath); err != nil {
mizu.Log.Errorf("Failed dump logs %v", err)
}

View File

@@ -1,9 +1,9 @@
package cmd
import (
"errors"
"fmt"
"github.com/spf13/cobra"
"github.com/up9inc/mizu/cli/fsUtils"
"github.com/up9inc/mizu/cli/mizu"
)
@@ -13,9 +13,12 @@ var rootCmd = &cobra.Command{
Long: `A web traffic viewer for kubernetes
Further info is available at https://github.com/up9inc/mizu`,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if err := fsUtils.EnsureDir(mizu.GetMizuFolderPath()); err != nil {
mizu.Log.Errorf("Failed to use mizu folder, %v", err)
}
mizu.InitLogger()
if err := mizu.InitConfig(cmd); err != nil {
mizu.Log.Errorf("Invalid config, Exit %s", err)
return errors.New(fmt.Sprintf("%v", err))
mizu.Log.Fatal(err)
}
return nil

View File

@@ -63,7 +63,6 @@ func init() {
tapCmd.Flags().StringArrayP(configStructs.NamespacesTapName, "n", defaultTapConfig.Namespaces, "Namespaces selector")
tapCmd.Flags().Bool(configStructs.AnalysisTapName, defaultTapConfig.Analysis, "Uploads traffic to UP9 for further analysis (Beta)")
tapCmd.Flags().BoolP(configStructs.AllNamespacesTapName, "A", defaultTapConfig.AllNamespaces, "Tap all namespaces")
tapCmd.Flags().StringP(configStructs.KubeConfigPathTapName, "k", defaultTapConfig.KubeConfigPath, "Path to kube-config file")
tapCmd.Flags().StringArrayP(configStructs.PlainTextFilterRegexesTapName, "r", defaultTapConfig.PlainTextFilterRegexes, "List of regex expressions that are used to filter matching values from text/plain http bodies")
tapCmd.Flags().Bool(configStructs.HideHealthChecksTapName, defaultTapConfig.HideHealthChecks, "hides requests with kube-probe or prometheus user-agent headers")
tapCmd.Flags().Bool(configStructs.DisableRedactionTapName, defaultTapConfig.DisableRedaction, "Disables redaction of potentially sensitive request/response headers and body values")

View File

@@ -5,6 +5,9 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/up9inc/mizu/cli/fsUtils"
"github.com/up9inc/mizu/cli/goUtils"
"github.com/up9inc/mizu/cli/mizu/configStructs"
"net/http"
"net/url"
"os"
@@ -17,7 +20,6 @@ import (
"github.com/up9inc/mizu/cli/errormessage"
"github.com/up9inc/mizu/cli/kubernetes"
"github.com/up9inc/mizu/cli/logsUtils"
"github.com/up9inc/mizu/cli/mizu"
"github.com/up9inc/mizu/cli/uiUtils"
"github.com/up9inc/mizu/shared"
@@ -56,13 +58,12 @@ func RunMizuTap() {
}
}
kubernetesProvider, err := kubernetes.NewProvider(mizu.Config.Tap.KubeConfigPath)
kubernetesProvider, err := kubernetes.NewProvider(mizu.Config.KubeConfigPath)
if err != nil {
mizu.Log.Error(err)
return
}
defer cleanUpMizuResources(kubernetesProvider)
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // cancel will be called when this function exits
@@ -96,13 +97,14 @@ func RunMizuTap() {
nodeToTappedPodIPMap := getNodeHostToTappedPodIpsMap(state.currentlyTappedPods)
defer cleanUpMizuResources(kubernetesProvider)
if err := createMizuResources(ctx, kubernetesProvider, nodeToTappedPodIPMap, mizuApiFilteringOptions, mizuValidationRules); err != nil {
mizu.Log.Errorf(uiUtils.Error, fmt.Sprintf("Error creating resources: %v", errormessage.FormatError(err)))
return
}
go createProxyToApiServerPod(ctx, kubernetesProvider, cancel)
go watchPodsForTapping(ctx, kubernetesProvider, targetNamespaces, cancel)
go goUtils.HandleExcWrapper(createProxyToApiServerPod, ctx, kubernetesProvider, cancel)
go goUtils.HandleExcWrapper(watchPodsForTapping, ctx, kubernetesProvider, targetNamespaces, cancel)
//block until exit signal or error
waitForFinish(ctx, cancel)
@@ -248,7 +250,7 @@ func cleanUpMizuResources(kubernetesProvider *kubernetes.Provider) {
if mizu.Config.DumpLogs {
mizuDir := mizu.GetMizuFolderPath()
filePath = path.Join(mizuDir, fmt.Sprintf("mizu_logs_%s.zip", time.Now().Format("2006_01_02__15_04_05")))
if err := logsUtils.DumpLogs(kubernetesProvider, removalCtx, filePath); err != nil {
if err := fsUtils.DumpLogs(kubernetesProvider, removalCtx, filePath); err != nil {
mizu.Log.Errorf("Failed dump logs %v", err)
}
}
@@ -327,7 +329,7 @@ func waitUntilNamespaceDeleted(ctx context.Context, cancel context.CancelFunc, k
}
func reportTappedPods() {
mizuProxiedUrl := kubernetes.GetMizuApiServerProxiedHostAndPath(mizu.Config.Fetch.MizuPort)
mizuProxiedUrl := kubernetes.GetMizuApiServerProxiedHostAndPath(mizu.Config.Tap.GuiPort)
tappedPodsUrl := fmt.Sprintf("http://%s/status/tappedPods", mizuProxiedUrl)
podInfos := make([]shared.PodInfo, 0)
@@ -355,7 +357,7 @@ func watchPodsForTapping(ctx context.Context, kubernetesProvider *kubernetes.Pro
restartTappers := func() {
err, changeFound := updateCurrentlyTappedPods(kubernetesProvider, ctx, targetNamespaces)
if err != nil {
mizu.Log.Errorf(uiUtils.Error, fmt.Sprintf("Error getting pods by regex: %v", errormessage.FormatError(err)))
mizu.Log.Errorf(uiUtils.Error, fmt.Sprintf("Failed to update currently tapped pods: %v", err))
cancel()
}
@@ -398,11 +400,15 @@ func watchPodsForTapping(ctx context.Context, kubernetesProvider *kubernetes.Pro
restartTappersDebouncer.SetOn()
}
case <-errorChan:
case err := <-errorChan:
mizu.Log.Debugf("Watching pods loop, got error %v, stopping `restart tappers debouncer`", err)
restartTappersDebouncer.Cancel()
// TODO: Does this also perform cleanup?
cancel()
case <-ctx.Done():
mizu.Log.Debugf("Watching pods loop, context done, stopping `restart tappers debouncer`")
restartTappersDebouncer.Cancel()
return
}
}
@@ -413,7 +419,8 @@ func updateCurrentlyTappedPods(kubernetesProvider *kubernetes.Provider, ctx cont
if matchingPods, err := kubernetesProvider.ListAllRunningPodsMatchingRegex(ctx, mizu.Config.Tap.PodRegex(), targetNamespaces); err != nil {
return err, false
} else {
addedPods, removedPods := getPodArrayDiff(state.currentlyTappedPods, matchingPods)
podsToTap := excludeMizuPods(matchingPods)
addedPods, removedPods := getPodArrayDiff(state.currentlyTappedPods, podsToTap)
for _, addedPod := range addedPods {
changeFound = true
mizu.Log.Infof(uiUtils.Green, fmt.Sprintf("+%s", addedPod.Name))
@@ -422,12 +429,25 @@ func updateCurrentlyTappedPods(kubernetesProvider *kubernetes.Provider, ctx cont
changeFound = true
mizu.Log.Infof(uiUtils.Red, fmt.Sprintf("-%s", removedPod.Name))
}
state.currentlyTappedPods = matchingPods
state.currentlyTappedPods = podsToTap
}
return nil, changeFound
}
func excludeMizuPods(pods []core.Pod) []core.Pod {
mizuPrefixRegex := regexp.MustCompile("^" + mizu.MizuResourcesPrefix)
nonMizuPods := make([]core.Pod, 0)
for _, pod := range pods {
if !mizuPrefixRegex.MatchString(pod.Name) {
nonMizuPods = append(nonMizuPods, pod)
}
}
return nonMizuPods
}
func getPodArrayDiff(oldPods []core.Pod, newPods []core.Pod) (added []core.Pod, removed []core.Pod) {
added = getMissingPods(newPods, oldPods)
removed = getMissingPods(oldPods, newPods)
@@ -461,35 +481,32 @@ func createProxyToApiServerPod(ctx context.Context, kubernetesProvider *kubernet
for {
select {
case <-ctx.Done():
mizu.Log.Debugf("Watching API Server pod loop, ctx done")
return
case <-added:
mizu.Log.Debugf("Got agent pod added event")
mizu.Log.Debugf("Watching API Server pod loop, added")
continue
case <-removed:
mizu.Log.Infof("%s removed", mizu.ApiServerPodName)
cancel()
return
case modifiedPod := <-modified:
mizu.Log.Debugf("Got agent pod modified event, status phase: %v", modifiedPod.Status.Phase)
if modifiedPod == nil {
mizu.Log.Debugf("Watching API Server pod loop, modifiedPod with nil")
continue
}
mizu.Log.Debugf("Watching API Server pod loop, modified: %v", modifiedPod.Status.Phase)
if modifiedPod.Status.Phase == core.PodRunning && !isPodReady {
isPodReady = true
go func() {
err := kubernetes.StartProxy(kubernetesProvider, mizu.Config.Tap.GuiPort, mizu.Config.MizuResourcesNamespace, mizu.ApiServerPodName)
if err != nil {
mizu.Log.Errorf(uiUtils.Error, fmt.Sprintf("Error occured while running k8s proxy %v", errormessage.FormatError(err)))
cancel()
}
}()
mizuProxiedUrl := kubernetes.GetMizuApiServerProxiedHostAndPath(mizu.Config.Tap.GuiPort)
mizu.Log.Infof("Mizu is available at http://%s\n", mizuProxiedUrl)
go startProxyReportErrorIfAny(kubernetesProvider, cancel)
mizu.Log.Infof("Mizu is available at http://%s\n", kubernetes.GetMizuApiServerProxiedHostAndPath(mizu.Config.Tap.GuiPort))
time.Sleep(time.Second * 5) // Waiting to be sure the proxy is ready
requestForAnalysis()
reportTappedPods()
}
case <-timeAfter:
if !isPodReady {
mizu.Log.Errorf(uiUtils.Error, fmt.Sprintf("%s pod was not ready in time", mizu.ApiServerPodName))
mizu.Log.Errorf(uiUtils.Error, "Mizu API server was not ready in time")
cancel()
}
case <-errorChan:
@@ -499,6 +516,15 @@ func createProxyToApiServerPod(ctx context.Context, kubernetesProvider *kubernet
}
}
func startProxyReportErrorIfAny(kubernetesProvider *kubernetes.Provider, cancel context.CancelFunc) {
err := kubernetes.StartProxy(kubernetesProvider, mizu.Config.Tap.GuiPort, mizu.Config.MizuResourcesNamespace, mizu.ApiServerPodName)
if err != nil {
mizu.Log.Errorf(uiUtils.Error, fmt.Sprintf("Error occured while running k8s proxy %v\n"+
"Try setting different port by using --%s", errormessage.FormatError(err), configStructs.GuiPortTapName))
cancel()
}
}
func requestForAnalysis() {
if !mizu.Config.Tap.Analysis {
return
@@ -522,21 +548,15 @@ func requestForAnalysis() {
}
func createRBACIfNecessary(ctx context.Context, kubernetesProvider *kubernetes.Provider) (bool, error) {
mizuRBACExists, err := kubernetesProvider.DoesServiceAccountExist(ctx, mizu.Config.MizuResourcesNamespace, mizu.ServiceAccountName)
if err != nil {
return false, err
}
if !mizuRBACExists {
if !mizu.Config.IsNsRestrictedMode() {
err := kubernetesProvider.CreateMizuRBAC(ctx, mizu.Config.MizuResourcesNamespace, mizu.ServiceAccountName, mizu.ClusterRoleName, mizu.ClusterRoleBindingName, mizu.RBACVersion)
if err != nil {
return false, err
}
} else {
err := kubernetesProvider.CreateMizuRBACNamespaceRestricted(ctx, mizu.Config.MizuResourcesNamespace, mizu.ServiceAccountName, mizu.RoleName, mizu.RoleBindingName, mizu.RBACVersion)
if err != nil {
return false, err
}
if !mizu.Config.IsNsRestrictedMode() {
err := kubernetesProvider.CreateMizuRBAC(ctx, mizu.Config.MizuResourcesNamespace, mizu.ServiceAccountName, mizu.ClusterRoleName, mizu.ClusterRoleBindingName, mizu.RBACVersion)
if err != nil {
return false, err
}
} else {
err := kubernetesProvider.CreateMizuRBACNamespaceRestricted(ctx, mizu.Config.MizuResourcesNamespace, mizu.ServiceAccountName, mizu.RoleName, mizu.RoleBindingName, mizu.RBACVersion)
if err != nil {
return false, err
}
}
return true, nil

View File

@@ -12,11 +12,6 @@ var viewCmd = &cobra.Command{
Short: "Open GUI in browser",
RunE: func(cmd *cobra.Command, args []string) error {
go mizu.ReportRun("view", mizu.Config.View)
if isCompatible, err := mizu.CheckVersionCompatibility(mizu.Config.View.GuiPort); err != nil {
return err
} else if !isCompatible {
return nil
}
runMizuView()
return nil
},

View File

@@ -20,10 +20,13 @@ func runMizuView() {
exists, err := kubernetesProvider.DoesServicesExist(ctx, mizu.Config.MizuResourcesNamespace, mizu.ApiServerPodName)
if err != nil {
panic(err)
mizu.Log.Errorf("Failed to found mizu service %v", err)
cancel()
return
}
if !exists {
mizu.Log.Infof("The %s service not found", mizu.ApiServerPodName)
mizu.Log.Infof("%s service not found, you should run `mizu tap` command first", mizu.ApiServerPodName)
cancel()
return
}
@@ -33,11 +36,19 @@ func runMizuView() {
mizu.Log.Infof("Found a running service %s and open port %d", mizu.ApiServerPodName, mizu.Config.View.GuiPort)
return
}
mizu.Log.Infof("Found service %s, creating k8s proxy", mizu.ApiServerPodName)
mizu.Log.Debugf("Found service %s, creating k8s proxy", mizu.ApiServerPodName)
go startProxyReportErrorIfAny(kubernetesProvider, cancel)
mizu.Log.Infof("Mizu is available at http://%s\n", kubernetes.GetMizuApiServerProxiedHostAndPath(mizu.Config.View.GuiPort))
err = kubernetes.StartProxy(kubernetesProvider, mizu.Config.View.GuiPort, mizu.Config.MizuResourcesNamespace, mizu.ApiServerPodName)
if err != nil {
mizu.Log.Infof("Error occured while running k8s proxy %v", err)
if isCompatible, err := mizu.CheckVersionCompatibility(mizu.Config.View.GuiPort); err != nil {
mizu.Log.Errorf("Failed to check versions compatibility %v", err)
cancel()
return
} else if !isCompatible {
cancel()
return
}
waitForFinish(ctx, cancel)
}

View File

@@ -17,8 +17,12 @@ func FormatError(err error) error {
var errorNew error
if k8serrors.IsForbidden(err) {
errorNew = fmt.Errorf("insufficient permissions: %w. "+
"supply the required permission or control Mizu's access to namespaces by setting MizuResourcesNamespace "+
"in the config file or setting the tapped namespace with --%s %s=<NAMEPSACE>", err, mizu.SetCommandName, mizu.MizuResourcesNamespaceConfigName)
"supply the required permission or control Mizu's access to namespaces by setting %s "+
"in the config file or setting the tapped namespace with --%s %s=<NAMEPSACE>",
err,
mizu.MizuResourcesNamespaceConfigName,
mizu.SetCommandName,
mizu.MizuResourcesNamespaceConfigName)
} else if syntaxError, isSyntaxError := asRegexSyntaxError(err); isSyntaxError {
errorNew = fmt.Errorf("regex %s is invalid: %w", syntaxError.Expr, err)
} else {

26
cli/fsUtils/dirUtils.go Normal file
View File

@@ -0,0 +1,26 @@
package fsUtils
import (
"errors"
"fmt"
"os"
)
func EnsureDir(dirName string) error {
err := os.Mkdir(dirName, 0700)
if err == nil {
return nil
}
if os.IsExist(err) {
// check that the existing path is a directory
info, err := os.Stat(dirName)
if err != nil {
return err
}
if !info.IsDir() {
return errors.New(fmt.Sprintf("path exists but is not a directory: %s", dirName))
}
return nil
}
return err
}

View File

@@ -0,0 +1,58 @@
package fsUtils
import (
"archive/zip"
"context"
"fmt"
"github.com/up9inc/mizu/cli/kubernetes"
"github.com/up9inc/mizu/cli/mizu"
"os"
"regexp"
)
func DumpLogs(provider *kubernetes.Provider, ctx context.Context, filePath string) error {
podExactRegex := regexp.MustCompile("^" + mizu.MizuResourcesPrefix)
pods, err := provider.ListAllPodsMatchingRegex(ctx, podExactRegex, []string{mizu.Config.MizuResourcesNamespace})
if err != nil {
return err
}
if len(pods) == 0 {
return fmt.Errorf("no mizu pods found in namespace %s", mizu.Config.MizuResourcesNamespace)
}
newZipFile, err := os.Create(filePath)
if err != nil {
return err
}
defer newZipFile.Close()
zipWriter := zip.NewWriter(newZipFile)
defer zipWriter.Close()
for _, pod := range pods {
logs, err := provider.GetPodLogs(pod.Namespace, pod.Name, ctx)
if err != nil {
mizu.Log.Errorf("Failed to get logs, %v", err)
continue
} else {
mizu.Log.Debugf("Successfully read log length %d for pod: %s.%s", len(logs), pod.Namespace, pod.Name)
}
if err := AddStrToZip(zipWriter, logs, fmt.Sprintf("%s.%s.log", pod.Namespace, pod.Name)); err != nil {
mizu.Log.Errorf("Failed write logs, %v", err)
} else {
mizu.Log.Infof("Successfully added log length %d from pod: %s.%s", len(logs), pod.Namespace, pod.Name)
}
}
if err := AddFileToZip(zipWriter, mizu.GetConfigFilePath()); err != nil {
mizu.Log.Debugf("Failed write file, %v", err)
} else {
mizu.Log.Infof("Successfully added file %s", mizu.GetConfigFilePath())
}
if err := AddFileToZip(zipWriter, mizu.GetLogFilePath()); err != nil {
mizu.Log.Debugf("Failed write file, %v", err)
} else {
mizu.Log.Infof("Successfully added file %s", mizu.GetLogFilePath())
}
mizu.Log.Infof("You can find the zip with all logs in %s\n", filePath)
return nil
}

55
cli/fsUtils/zipUtils.go Normal file
View File

@@ -0,0 +1,55 @@
package fsUtils
import (
"archive/zip"
"fmt"
"io"
"os"
"path/filepath"
)
func AddFileToZip(zipWriter *zip.Writer, filename string) error {
fileToZip, err := os.Open(filename)
if err != nil {
return fmt.Errorf("failed to open file %s, %w", filename, err)
}
defer fileToZip.Close()
// Get the file information
info, err := fileToZip.Stat()
if err != nil {
return fmt.Errorf("failed to get file information %s, %w", filename, err)
}
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
// Using FileInfoHeader() above only uses the basename of the file. If we want
// to preserve the folder structure we can overwrite this with the full path.
header.Name = filepath.Base(filename)
// Change to deflate to gain better compression
// see http://golang.org/pkg/archive/zip/#pkg-constants
header.Method = zip.Deflate
writer, err := zipWriter.CreateHeader(header)
if err != nil {
return fmt.Errorf("failed to create header in zip for %s, %w", filename, err)
}
_, err = io.Copy(writer, fileToZip)
return err
}
func AddStrToZip(writer *zip.Writer, logs string, fileName string) error {
if zipFile, err := writer.Create(fileName); err != nil {
return fmt.Errorf("couldn't create a log file inside zip for %s, %w", fileName, err)
} else {
if _, err = zipFile.Write([]byte(logs)); err != nil {
return fmt.Errorf("couldn't write logs to zip file: %s, %w", fileName, err)
}
}
return nil
}

View File

@@ -0,0 +1,25 @@
package goUtils
import (
"github.com/up9inc/mizu/cli/mizu"
"reflect"
"runtime/debug"
)
func HandleExcWrapper(fn interface{}, params ...interface{}) (result []reflect.Value) {
defer func() {
if panicMessage := recover(); panicMessage != nil {
stack := debug.Stack()
mizu.Log.Fatalf("Unhandled panic: %v\n stack: %s", panicMessage, stack)
}
}()
f := reflect.ValueOf(fn)
if f.Type().NumIn() != len(params) {
panic("incorrect number of parameters!")
}
inputs := make([]reflect.Value, len(params))
for k, in := range params {
inputs[k] = reflect.ValueOf(in)
}
return f.Call(inputs)
}

View File

@@ -13,17 +13,14 @@ import (
"strconv"
"github.com/up9inc/mizu/cli/mizu"
"io"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/homedir"
"github.com/up9inc/mizu/shared"
"io"
core "k8s.io/api/core/v1"
rbac "k8s.io/api/rbac/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
resource "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/watch"
applyconfapp "k8s.io/client-go/applyconfigurations/apps/v1"
@@ -35,9 +32,11 @@ import (
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
_ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/clientcmd"
_ "k8s.io/client-go/tools/portforward"
watchtools "k8s.io/client-go/tools/watch"
"k8s.io/client-go/util/homedir"
)
type Provider struct {
@@ -358,15 +357,15 @@ func (provider *Provider) CreateMizuRBAC(ctx context.Context, namespace string,
},
}
_, err := provider.clientSet.CoreV1().ServiceAccounts(namespace).Create(ctx, serviceAccount, metav1.CreateOptions{})
if err != nil {
if err != nil && !k8serrors.IsAlreadyExists(err) {
return err
}
_, err = provider.clientSet.RbacV1().ClusterRoles().Create(ctx, clusterRole, metav1.CreateOptions{})
if err != nil {
if err != nil && !k8serrors.IsAlreadyExists(err) {
return err
}
_, err = provider.clientSet.RbacV1().ClusterRoleBindings().Create(ctx, clusterRoleBinding, metav1.CreateOptions{})
if err != nil {
if err != nil && !k8serrors.IsAlreadyExists(err) {
return err
}
return nil
@@ -412,15 +411,15 @@ func (provider *Provider) CreateMizuRBACNamespaceRestricted(ctx context.Context,
},
}
_, err := provider.clientSet.CoreV1().ServiceAccounts(namespace).Create(ctx, serviceAccount, metav1.CreateOptions{})
if err != nil {
if err != nil && !k8serrors.IsAlreadyExists(err) {
return err
}
_, err = provider.clientSet.RbacV1().Roles(namespace).Create(ctx, role, metav1.CreateOptions{})
if err != nil {
if err != nil && !k8serrors.IsAlreadyExists(err) {
return err
}
_, err = provider.clientSet.RbacV1().RoleBindings(namespace).Create(ctx, roleBinding, metav1.CreateOptions{})
if err != nil {
if err != nil && !k8serrors.IsAlreadyExists(err) {
return err
}
return nil
@@ -683,7 +682,7 @@ func (provider *Provider) ListAllPodsMatchingRegex(ctx context.Context, regex *r
for _, namespace := range namespaces {
namespacePods, err := provider.clientSet.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get pods in ns: %s, %w", namespace, err)
return nil, fmt.Errorf("failed to get pods in ns: [%s], %w", namespace, err)
}
pods = append(pods, namespacePods.Items...)

View File

@@ -1,106 +0,0 @@
package logsUtils
import (
"archive/zip"
"context"
"fmt"
"github.com/up9inc/mizu/cli/kubernetes"
"github.com/up9inc/mizu/cli/mizu"
"io"
"os"
"path/filepath"
"regexp"
)
func DumpLogs(provider *kubernetes.Provider, ctx context.Context, filePath string) error {
podExactRegex := regexp.MustCompile(fmt.Sprintf("^mizu-"))
pods, err := provider.ListAllPodsMatchingRegex(ctx, podExactRegex, []string{mizu.Config.MizuResourcesNamespace})
if err != nil {
return err
}
if len(pods) == 0 {
return fmt.Errorf("no pods found in namespace %s", mizu.Config.MizuResourcesNamespace)
}
newZipFile, err := os.Create(filePath)
if err != nil {
return err
}
defer newZipFile.Close()
zipWriter := zip.NewWriter(newZipFile)
defer zipWriter.Close()
for _, pod := range pods {
logs, err := provider.GetPodLogs(pod.Namespace, pod.Name, ctx)
if err != nil {
mizu.Log.Errorf("Failed to get logs, %v", err)
continue
} else {
mizu.Log.Debugf("Successfully read log length %d for pod: %s.%s", len(logs), pod.Namespace, pod.Name)
}
if err := addLogsToZip(zipWriter, logs, fmt.Sprintf("%s.%s.log", pod.Namespace, pod.Name)); err != nil {
mizu.Log.Errorf("Failed write logs, %v", err)
} else {
mizu.Log.Infof("Successfully added log length %d from pod: %s.%s", len(logs), pod.Namespace, pod.Name)
}
}
if err := addFileToZip(zipWriter, mizu.GetConfigFilePath()); err != nil {
mizu.Log.Errorf("Failed write file, %v", err)
} else {
mizu.Log.Infof("Successfully added file %s", mizu.GetConfigFilePath())
}
if err := addFileToZip(zipWriter, mizu.GetLogFilePath()); err != nil {
mizu.Log.Errorf("Failed write file, %v", err)
} else {
mizu.Log.Infof("Successfully added file %s", mizu.GetLogFilePath())
}
mizu.Log.Infof("You can find the zip with all logs in %s\n", filePath)
return nil
}
func addFileToZip(zipWriter *zip.Writer, filename string) error {
fileToZip, err := os.Open(filename)
if err != nil {
return fmt.Errorf("failed to open file %s, %w", filename, err)
}
defer fileToZip.Close()
// Get the file information
info, err := fileToZip.Stat()
if err != nil {
return fmt.Errorf("failed to get file information %s, %w", filename, err)
}
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
// Using FileInfoHeader() above only uses the basename of the file. If we want
// to preserve the folder structure we can overwrite this with the full path.
header.Name = filepath.Base(filename)
// Change to deflate to gain better compression
// see http://golang.org/pkg/archive/zip/#pkg-constants
header.Method = zip.Deflate
writer, err := zipWriter.CreateHeader(header)
if err != nil {
return fmt.Errorf("failed to create header in zip for %s, %w", filename, err)
}
_, err = io.Copy(writer, fileToZip)
return err
}
func addLogsToZip(writer *zip.Writer, logs string, fileName string) error {
if zipFile, err := writer.Create(fileName); err != nil {
return fmt.Errorf("couldn't create a log file inside zip for %s, %w", fileName, err)
} else {
if _, err = zipFile.Write([]byte(logs)); err != nil {
return fmt.Errorf("couldn't write logs to zip file: %s, %w", fileName, err)
}
}
return nil
}

View File

@@ -2,10 +2,9 @@ package main
import (
"github.com/up9inc/mizu/cli/cmd"
"github.com/up9inc/mizu/cli/mizu"
"github.com/up9inc/mizu/cli/goUtils"
)
func main() {
mizu.InitLogger()
cmd.Execute()
goUtils.HandleExcWrapper(cmd.Execute)
}

View File

@@ -28,6 +28,7 @@ var allowedSetFlags = []string{
MizuResourcesNamespaceConfigName,
TelemetryConfigName,
DumpLogsConfigName,
KubeConfigPathName,
configStructs.AnalysisDestinationTapName,
configStructs.SleepIntervalSecTapName,
}
@@ -37,7 +38,7 @@ var Config = ConfigStruct{}
func (config *ConfigStruct) Validate() error {
if config.IsNsRestrictedMode() {
if config.Tap.AllNamespaces || len(config.Tap.Namespaces) != 1 || config.Tap.Namespaces[0] != config.MizuResourcesNamespace {
return fmt.Errorf("Not supported mode. Mizu can't resolve IPs in other namespaces when running in namespace restricted mode.\n" +
return fmt.Errorf("Not supported mode. Mizu can't resolve IPs in other namespaces when running in namespace restricted mode.\n"+
"You can use the same namespace for --%s and --%s", configStructs.NamespacesTapName, MizuResourcesNamespaceConfigName)
}
}
@@ -51,8 +52,8 @@ func InitConfig(cmd *cobra.Command) error {
}
if err := mergeConfigFile(); err != nil {
Log.Errorf("Could not load config file, error %v", err)
Log.Fatalf("You can regenerate the file using `mizu config -r` or just remove it %v", GetConfigFilePath())
return fmt.Errorf("invalid config %w\n"+
"you can regenerate the file using `mizu config -r` or just remove it %v", err, GetConfigFilePath())
}
cmd.Flags().Visit(initFlag)
@@ -104,38 +105,34 @@ func initFlag(f *pflag.Flag) {
}
if f.Name == SetCommandName {
if setError := mergeSetFlag(sliceValue.GetSlice()); setError != nil {
Log.Warningf(uiUtils.Red, fmt.Sprintf("%v", setError))
}
mergeSetFlag(sliceValue.GetSlice())
return
}
mergeFlagValues(configElem, f.Name, sliceValue.GetSlice())
}
func mergeSetFlag(setValues []string) error {
func mergeSetFlag(setValues []string) {
configElem := reflect.ValueOf(&Config).Elem()
for _, setValue := range setValues {
if !strings.Contains(setValue, Separator) {
return errors.New(fmt.Sprintf("invalid set argument %s", setValue))
Log.Warningf(uiUtils.Warning, fmt.Sprintf("Ignoring set argument %s (set argument format: <flag name>=<flag value>)", setValue))
}
split := strings.SplitN(setValue, Separator, 2)
if len(split) != 2 {
return errors.New(fmt.Sprintf("invalid set argument %s", setValue))
Log.Warningf(uiUtils.Warning, fmt.Sprintf("Ignoring set argument %s (set argument format: <flag name>=<flag value>)", setValue))
}
argumentKey, argumentValue := split[0], split[1]
if !Contains(allowedSetFlags, argumentKey) {
return errors.New(fmt.Sprintf("invalid set flag name %s, allowed set flag names: \"%s\"", argumentKey, strings.Join(allowedSetFlags, "\", \"")))
Log.Warningf(uiUtils.Warning, fmt.Sprintf("Ignoring set argument %s, flag name must be one of the following: \"%s\"", setValue, strings.Join(allowedSetFlags, "\", \"")))
}
mergeFlagValue(configElem, argumentKey, argumentValue)
}
return nil
}
func mergeFlagValue(currentElem reflect.Value, flagKey string, flagValue string) {
@@ -206,14 +203,70 @@ func getParsedValue(kind reflect.Kind, value string) (reflect.Value, error) {
}
return reflect.ValueOf(boolArgumentValue), nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
case reflect.Int:
intArgumentValue, err := strconv.ParseInt(value, 10, 64)
if err != nil {
break
}
return reflect.ValueOf(int(intArgumentValue)), nil
case reflect.Int8:
intArgumentValue, err := strconv.ParseInt(value, 10, 8)
if err != nil {
break
}
return reflect.ValueOf(int8(intArgumentValue)), nil
case reflect.Int16:
intArgumentValue, err := strconv.ParseInt(value, 10, 16)
if err != nil {
break
}
return reflect.ValueOf(int16(intArgumentValue)), nil
case reflect.Int32:
intArgumentValue, err := strconv.ParseInt(value, 10, 32)
if err != nil {
break
}
return reflect.ValueOf(int32(intArgumentValue)), nil
case reflect.Int64:
intArgumentValue, err := strconv.ParseInt(value, 10, 64)
if err != nil {
break
}
return reflect.ValueOf(intArgumentValue), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
case reflect.Uint:
uintArgumentValue, err := strconv.ParseUint(value, 10, 64)
if err != nil {
break
}
return reflect.ValueOf(uint(uintArgumentValue)), nil
case reflect.Uint8:
uintArgumentValue, err := strconv.ParseUint(value, 10, 8)
if err != nil {
break
}
return reflect.ValueOf(uint8(uintArgumentValue)), nil
case reflect.Uint16:
uintArgumentValue, err := strconv.ParseUint(value, 10, 16)
if err != nil {
break
}
return reflect.ValueOf(uint16(uintArgumentValue)), nil
case reflect.Uint32:
uintArgumentValue, err := strconv.ParseUint(value, 10, 32)
if err != nil {
break
}
return reflect.ValueOf(uint32(uintArgumentValue)), nil
case reflect.Uint64:
uintArgumentValue, err := strconv.ParseUint(value, 10, 64)
if err != nil {
break

View File

@@ -11,6 +11,7 @@ const (
MizuResourcesNamespaceConfigName = "mizu-resources-namespace"
TelemetryConfigName = "telemetry"
DumpLogsConfigName = "dump-logs"
KubeConfigPathName = "kube-config-path"
)
type ConfigStruct struct {
@@ -22,6 +23,7 @@ type ConfigStruct struct {
MizuResourcesNamespace string `yaml:"mizu-resources-namespace" default:"mizu"`
Telemetry bool `yaml:"telemetry" default:"true"`
DumpLogs bool `yaml:"dump-logs" default:"false"`
KubeConfigPath string `yaml:"kube-config-path" default:""`
}
func (config *ConfigStruct) SetDefaults() {

View File

@@ -16,7 +16,6 @@ const (
NamespacesTapName = "namespaces"
AnalysisTapName = "analysis"
AllNamespacesTapName = "all-namespaces"
KubeConfigPathTapName = "kube-config"
PlainTextFilterRegexesTapName = "regex-masking"
HideHealthChecksTapName = "hide-healthchecks"
DisableRedactionTapName = "no-redact"
@@ -34,7 +33,6 @@ type TapConfig struct {
Namespaces []string `yaml:"namespaces"`
Analysis bool `yaml:"analysis" default:"false"`
AllNamespaces bool `yaml:"all-namespaces" default:"false"`
KubeConfigPath string `yaml:"kube-config"`
PlainTextFilterRegexes []string `yaml:"regex-masking"`
HideHealthChecks bool `yaml:"hide-healthchecks" default:"false"`
DisableRedaction bool `yaml:"no-redact" default:"false"`

View File

@@ -14,16 +14,17 @@ var (
)
const (
ApiServerPodName = "mizu-api-server"
ClusterRoleBindingName = "mizu-cluster-role-binding"
ClusterRoleName = "mizu-cluster-role"
MizuResourcesPrefix = "mizu-"
ApiServerPodName = MizuResourcesPrefix + "api-server"
ClusterRoleBindingName = MizuResourcesPrefix + "cluster-role-binding"
ClusterRoleName = MizuResourcesPrefix + "cluster-role"
K8sAllNamespaces = ""
RoleBindingName = "mizu-role-binding"
RoleName = "mizu-role"
ServiceAccountName = "mizu-service-account"
TapperDaemonSetName = "mizu-tapper-daemon-set"
TapperPodName = "mizu-tapper"
ConfigMapName = "mizu-policy"
RoleBindingName = MizuResourcesPrefix + "role-binding"
RoleName = MizuResourcesPrefix + "role"
ServiceAccountName = MizuResourcesPrefix + "service-account"
TapperDaemonSetName = MizuResourcesPrefix + "tapper-daemon-set"
TapperPodName = MizuResourcesPrefix + "tapper"
ConfigMapName = MizuResourcesPrefix + "policy"
)
func GetMizuFolderPath() string {

View File

@@ -1,7 +1,6 @@
package mizu
import (
"fmt"
"github.com/op/go-logging"
"os"
"path"
@@ -21,7 +20,7 @@ func InitLogger() {
logPath := GetLogFilePath()
f, err := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
panic(fmt.Sprintf("Failed mizu log file: %v, err %v", logPath, err))
Log.Infof("Failed to open mizu log file: %v, err %v", logPath, err)
}
fileLog := logging.NewLogBackend(f, "", 0)

View File

@@ -46,7 +46,7 @@ func CheckVersionCompatibility(port uint16) (bool, error) {
return true, nil
}
Log.Infof(uiUtils.Red, fmt.Sprintf("cli version (%s) is not compatible with api version (%s)", SemVer, apiSemVer))
Log.Errorf(uiUtils.Red, fmt.Sprintf("cli version (%s) is not compatible with api version (%s)", SemVer, apiSemVer))
return false, nil
}

View File

@@ -1,6 +1,7 @@
package debounce
import (
"fmt"
"time"
)
@@ -13,9 +14,10 @@ func NewDebouncer(timeout time.Duration, callback func()) *Debouncer {
type Debouncer struct {
callback func()
running bool
timeout time.Duration
timer *time.Timer
running bool
canceled bool
timeout time.Duration
timer *time.Timer
}
func (d *Debouncer) setTimeout(timeout time.Duration) {
@@ -25,18 +27,28 @@ func (d *Debouncer) setTimeout(timeout time.Duration) {
func (d *Debouncer) setCallback(callback func()) {
callbackWrapped := func() {
callback()
if !d.canceled {
callback()
}
d.running = false
}
d.callback = callbackWrapped
}
func (d *Debouncer) SetOn() {
func (d *Debouncer) Cancel() {
d.canceled = true
}
func (d *Debouncer) SetOn() error {
if d.canceled {
return fmt.Errorf("debouncer cancelled")
}
if d.running == true {
return
return nil
}
d.running = true
d.timer = time.AfterFunc(d.timeout, d.callback)
return nil
}