Skip to content

Commit 0ac8164

Browse files
Merge pull request #69 from Zenika/master
Release v1.8.0
2 parents dabc36f + 881bc11 commit 0ac8164

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+6298
-7721
lines changed

.circleci/config.yml

+9-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ jobs:
1212
- karto
1313
build-front:
1414
docker:
15-
- image: cimg/node:14.15.1
15+
- image: cimg/node:16.15.0
16+
resource_class: large
1617
working_directory: /home/circleci/karto
1718
steps:
1819
- attach_workspace:
@@ -33,7 +34,7 @@ jobs:
3334
- run:
3435
working_directory: front
3536
name: Run tests
36-
command: yarn test
37+
command: yarn test --maxWorkers=3
3738
- run:
3839
working_directory: front
3940
name: Build frontend
@@ -44,7 +45,8 @@ jobs:
4445
- karto/front/build
4546
build-back:
4647
docker:
47-
- image: cimg/go:1.17
48+
- image: cimg/go:1.18
49+
resource_class: large
4850
working_directory: /home/circleci/karto
4951
steps:
5052
- attach_workspace:
@@ -93,16 +95,16 @@ jobs:
9395
command: |
9496
docker build -t zenikalabs/karto .
9597
docker tag zenikalabs/karto zenikalabs/karto:v1
96-
docker tag zenikalabs/karto zenikalabs/karto:v1.7
97-
docker tag zenikalabs/karto zenikalabs/karto:v1.7.0
98+
docker tag zenikalabs/karto zenikalabs/karto:v1.8
99+
docker tag zenikalabs/karto zenikalabs/karto:v1.8.0
98100
- run:
99101
name: Push docker image
100102
command: |
101103
echo "$DOCKER_PASS" | docker login --username $DOCKER_USER --password-stdin
102104
docker push zenikalabs/karto
103105
docker push zenikalabs/karto:v1
104-
docker push zenikalabs/karto:v1.7
105-
docker push zenikalabs/karto:v1.7.0
106+
docker push zenikalabs/karto:v1.8
107+
docker push zenikalabs/karto:v1.8.0
106108
workflows:
107109
version: 2
108110
build-test-and-deploy:

README.md

+39-16
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ A simple static analysis tool to explore a Kubernetes cluster.
2424
## Main features
2525

2626
The left part of the screen contains the controls for the main view:
27+
2728
- View: choose your view
2829
- Workloads: deployments, controllers, pods, services, ingresses... and how they interact with each other
2930
- Network policies: network routes allowed between pods, based on network policy declarations
@@ -32,75 +33,88 @@ The left part of the screen contains the controls for the main view:
3233
- by pod namespace
3334
- by pod labels
3435
- by pod name
35-
- \[Network policies view only\] Include ingress neighbors: also display pods that can reach those in the current selection
36-
- \[Network policies view only\] Include egress neighbors: also display pods that can be reached by those in the current selection
36+
- \[Network policies view only\] Include ingress neighbors: also display pods that can reach those in the current
37+
selection
38+
- \[Network policies view only\] Include egress neighbors: also display pods that can be reached by those in the
39+
current selection
3740
- Display options: customize how items are displayed
3841
- Auto-refresh: automatically refresh the view every 2 seconds
3942
- Auto-zoom: automatically resize the view to fit all the elements to display
4043
- Show namespace prefix: add the namespace to the name of the displayed items
41-
- Always display large datasets: try to render the data even if the number of item is high (may slow down your browser)
44+
- Always display large datasets: try to render the data even if the number of item is high (may slow down your
45+
browser)
4246
- \[Network policies view only\] Highlight non isolated pods (ingress): color pods with no ingress network policy
4347
- \[Network policies view only\] Highlight non isolated pods (egress): color pods with no egress network policy
4448
- \[Health view only\] Highlight pods with container not running: color pods with at least one container not running
4549
- \[Health view only\] Highlight pods with container not ready: color pods with at least one container not ready
46-
- \[Health view only\] Highlight pods with container restarted: color pods with at least one container which restarted
50+
- \[Health view only\] Highlight pods with container restarted: color pods with at least one container which
51+
restarted
4752

4853
The main view shows the graph or list of items, depending on the selected view, filters and display options:
54+
4955
- Zoom in and out by scrolling
5056
- Drag and drop graph elements to draw the perfect map of your cluster
5157
- Hover over any graph element to display details: name, namespace, labels, isolation (ingress/egress)... and more!
5258

5359
In the top left part of the screen you will find action buttons to:
54-
- Export the current graph as PNG to use it in slides or share it
60+
61+
- Export the current graph as PNG to use it in slides or share it
5562
- Go fullscreen and use Karto as an office (or situation room) dashboard!
5663

5764
## Installation
5865

5966
There are two ways to install and run Karto:
60-
- To deploy it inside the Kubernetes cluster to analyze, proceed to the
61-
[Run inside a cluster](#run-inside-a-cluster) section.
62-
- To run it on any machine outside the Kubernetes cluster to analyze, refer to the
63-
[Run outside a cluster](#run-outside-a-cluster) section.
67+
68+
- To deploy it inside the Kubernetes cluster to analyze, proceed to the
69+
[Run inside a cluster](#run-inside-a-cluster) section.
70+
- To run it on any machine outside the Kubernetes cluster to analyze, refer to the
71+
[Run outside a cluster](#run-outside-a-cluster) section.
6472

6573
### Run inside a cluster
6674

6775
#### Deployment
6876

6977
Simply apply the provided descriptor:
78+
7079
```shell script
7180
kubectl apply -f deploy/k8s.yml
7281
```
82+
7383
This will:
84+
7485
- create a `karto` namespace
75-
- create a `karto` service account with a role allowing to watch the resources displayed by Karto (namespaces, pods,
86+
- create a `karto` service account with a role allowing to watch the resources displayed by Karto (namespaces, pods,
7687
network policies, services, deployments...)
7788
- deploy an instance of the application in this namespace with this service account
7889

7990
#### Exposition
8091

8192
Once deployed, the application must be exposed. For a quick try, use `port-forward`:
93+
8294
```shell script
8395
kubectl -n karto port-forward <pod name> 8000:8000
8496
```
97+
8598
The will exposed the app on your local machine on `localhost:8000`.
8699

87100
For a long-term solution, investigate the use of a [LoadBalancer service](
88101
https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) or an [Ingress](
89102
https://kubernetes.io/docs/concepts/services-networking/ingress/).
90103

91-
*Remember to always secure the access to the application as it obviously displays sensitive data about your cluster.*
104+
*Remember to always secure the access to the application as it obviously displays sensitive data about your cluster.*
92105

93106
#### Cleanup
94107

95108
Delete everything using the same descriptor:
109+
96110
```shell script
97111
kubectl delete -f deploy/k8s.yml
98112
```
99113

100114
### Run outside a cluster
101115

102116
For this to work, a local `kubeconfig` file with existing connection information to the target cluster must be present
103-
on the machine (if you already use `kubectl` locally, you are good to go!).
117+
on the machine (if you already use `kubectl` locally, you are good to go!).
104118

105119
Simply download the Karto binary from the [releases page](https://github.com/Zenika/karto/releases) and run it!
106120

@@ -109,28 +123,33 @@ Simply download the Karto binary from the [releases page](https://github.com/Zen
109123
### Prerequisites
110124

111125
The following tools must be available locally:
112-
- [Go](https://golang.org/doc/install) (tested with Go 1.17)
113-
- [NodeJS](https://nodejs.org/en/download/) (tested with NodeJS 14)
126+
127+
- [Go](https://golang.org/doc/install) (tested with Go 1.18)
128+
- [NodeJS](https://nodejs.org/en/download/) (tested with NodeJS 16)
114129

115130
### Run the frontend in dev mode
116131

117132
In the `front` directory, execute:
133+
118134
```shell script
119135
yarn start
120136
```
137+
121138
This will expose the app in dev mode on `localhost:3000` with a proxy to `localhost:8000` for the API calls.
122139

123140
### Run the backend locally
124141

125-
In the `back` directory, execute:
142+
In the `back` directory, execute:
143+
126144
```shell script
127145
go build karto
128146
./karto
129147
```
130148

131149
### Test suites
132150

133-
To run the entire backend test suite, execute in the `back` directory:
151+
To run the entire backend test suite, execute in the `back` directory:
152+
134153
```shell script
135154
go test ./...
136155
```
@@ -141,17 +160,21 @@ In production mode, the frontend is packaged in the go binary using [embed](http
141160
configuration, the frontend is served on the `/` route and the API on the `/api` route.
142161

143162
To compile the Karto binary from source, first compile the frontend source code. In the `front` directory, execute:
163+
144164
```shell script
145165
yarn build
146166
```
167+
147168
This will generate a `build` directory in `front`.
148169

149170
Then, make a copy in a directory visible by the backend module:
171+
150172
```shell script
151173
cp -R front/build/* back/exposition/frontend
152174
```
153175

154176
Finally, compile the go binary in the `back` directory:
177+
155178
```shell script
156179
go build karto
157180
```

back/analyzer/health/analyzer.go

+3-11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package health
33
import (
44
corev1 "k8s.io/api/core/v1"
55
"karto/analyzer/health/podhealth"
6+
"karto/commons"
67
"karto/types"
78
)
89

@@ -29,17 +30,8 @@ func NewAnalyzer(podHealthAnalyzer podhealth.Analyzer) Analyzer {
2930
}
3031

3132
func (analyzer analyzerImpl) Analyze(clusterState ClusterState) AnalysisResult {
32-
podHealths := analyzer.podHealthOfAllPods(clusterState.Pods)
33+
podsHealth := commons.Map(clusterState.Pods, analyzer.podHealthAnalyzer.Analyze)
3334
return AnalysisResult{
34-
Pods: podHealths,
35+
Pods: podsHealth,
3536
}
3637
}
37-
38-
func (analyzer analyzerImpl) podHealthOfAllPods(pods []*corev1.Pod) []*types.PodHealth {
39-
podHealths := make([]*types.PodHealth, 0)
40-
for _, pod := range pods {
41-
podHealth := analyzer.podHealthAnalyzer.Analyze(pod)
42-
podHealths = append(podHealths, podHealth)
43-
}
44-
return podHealths
45-
}

back/analyzer/health/podhealth/analyzer.go

+2-8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package podhealth
22

33
import (
44
corev1 "k8s.io/api/core/v1"
5+
"karto/analyzer/shared"
56
"karto/types"
67
)
78

@@ -30,17 +31,10 @@ func (analyzer analyzerImpl) Analyze(pod *corev1.Pod) *types.PodHealth {
3031
}
3132
}
3233
return &types.PodHealth{
33-
Pod: analyzer.toPodRef(pod),
34+
Pod: shared.ToPodRef(pod),
3435
Containers: containers,
3536
ContainersRunning: running,
3637
ContainersReady: ready,
3738
ContainersWithoutRestart: withoutRestart,
3839
}
3940
}
40-
41-
func (analyzer analyzerImpl) toPodRef(pod *corev1.Pod) types.PodRef {
42-
return types.PodRef{
43-
Name: pod.Name,
44-
Namespace: pod.Namespace,
45-
}
46-
}

back/analyzer/pod/analyzer.go

+3-9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package pod
22

33
import (
44
corev1 "k8s.io/api/core/v1"
5+
"karto/commons"
56
"karto/types"
67
)
78

@@ -24,19 +25,12 @@ func NewAnalyzer() Analyzer {
2425
}
2526

2627
func (analyzer analyzerImpl) Analyze(clusterState ClusterState) AnalysisResult {
28+
Pods := commons.Map(clusterState.Pods, analyzer.toPod)
2729
return AnalysisResult{
28-
Pods: analyzer.toPods(clusterState.Pods),
30+
Pods: Pods,
2931
}
3032
}
3133

32-
func (analyzer analyzerImpl) toPods(pods []*corev1.Pod) []*types.Pod {
33-
result := make([]*types.Pod, 0)
34-
for _, pod := range pods {
35-
result = append(result, analyzer.toPod(pod))
36-
}
37-
return result
38-
}
39-
4034
func (analyzer analyzerImpl) toPod(pod *corev1.Pod) *types.Pod {
4135
return &types.Pod{
4236
Name: pod.Name,
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1-
package utils
1+
package shared
22

33
import (
44
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
55
"k8s.io/apimachinery/pkg/labels"
6+
"karto/commons"
67
)
78

89
func SelectorMatches(objectLabels map[string]string, labelSelector metav1.LabelSelector) bool {
910
selector, _ := metav1.LabelSelectorAsSelector(&labelSelector)
1011
return selector.Matches(labels.Set(objectLabels))
1112
}
13+
14+
func IsOwnedBy(objectOwned metav1.Object, objectOwning metav1.Object) bool {
15+
return commons.AnyMatch(objectOwned.GetOwnerReferences(), func(ownerReference metav1.OwnerReference) bool {
16+
return ownerReference.UID == objectOwning.GetUID()
17+
})
18+
}

back/analyzer/traffic/shared/types.go back/analyzer/shared/podisolation.go

+3-10
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,9 @@ func (podIsolation *PodIsolation) AddEgressPolicy(egressPolicy *networkingv1.Net
2828
podIsolation.EgressPolicies = append(podIsolation.EgressPolicies, egressPolicy)
2929
}
3030

31-
func (podIsolation *PodIsolation) ToPodRef() types.PodRef {
32-
return types.PodRef{
33-
Name: podIsolation.Pod.Name,
34-
Namespace: podIsolation.Pod.Namespace,
35-
}
36-
}
37-
3831
func (podIsolation *PodIsolation) ToPodIsolation() *types.PodIsolation {
3932
return &types.PodIsolation{
40-
Pod: podIsolation.ToPodRef(),
33+
Pod: ToPodRef(podIsolation.Pod),
4134
IsIngressIsolated: podIsolation.IsIngressIsolated(),
4235
IsEgressIsolated: podIsolation.IsEgressIsolated(),
4336
}
@@ -46,7 +39,7 @@ func (podIsolation *PodIsolation) ToPodIsolation() *types.PodIsolation {
4639
func NewPodIsolation(pod *corev1.Pod) PodIsolation {
4740
return PodIsolation{
4841
Pod: pod,
49-
IngressPolicies: make([]*networkingv1.NetworkPolicy, 0),
50-
EgressPolicies: make([]*networkingv1.NetworkPolicy, 0),
42+
IngressPolicies: []*networkingv1.NetworkPolicy{},
43+
EgressPolicies: []*networkingv1.NetworkPolicy{},
5144
}
5245
}

back/analyzer/shared/ref.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package shared
2+
3+
import (
4+
appsv1 "k8s.io/api/apps/v1"
5+
corev1 "k8s.io/api/core/v1"
6+
"karto/types"
7+
)
8+
9+
func ToPodRef(pod *corev1.Pod) types.PodRef {
10+
return types.PodRef{
11+
Name: pod.Name,
12+
Namespace: pod.Namespace,
13+
}
14+
}
15+
16+
func ToServiceRef(service *corev1.Service) types.ServiceRef {
17+
return types.ServiceRef{
18+
Name: service.Name,
19+
Namespace: service.Namespace,
20+
}
21+
}
22+
23+
func ToReplicaSetRef(replicaSet *appsv1.ReplicaSet) types.ReplicaSetRef {
24+
return types.ReplicaSetRef{
25+
Name: replicaSet.Name,
26+
Namespace: replicaSet.Namespace,
27+
}
28+
}

0 commit comments

Comments
 (0)