mirror of
https://github.com/kubeshark/kubeshark.git
synced 2026-03-05 04:02:13 +00:00
Compare commits
101 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
346e904e77 | ||
|
|
c5471c501b | ||
|
|
1597321e24 | ||
|
|
a3111dee35 | ||
|
|
f37abfff88 | ||
|
|
817c17ad32 | ||
|
|
51658db0bd | ||
|
|
b2a4af8600 | ||
|
|
60e7890e23 | ||
|
|
f19c2f08c3 | ||
|
|
d8c0132a98 | ||
|
|
c5a36a494a | ||
|
|
a54cb917d0 | ||
|
|
30a85a4b92 | ||
|
|
cdbacff996 | ||
|
|
cf127c781c | ||
|
|
852a5ff045 | ||
|
|
371e513249 | ||
|
|
97cce32e3f | ||
|
|
d2e91b4ffa | ||
|
|
d5a42a66de | ||
|
|
f01962085a | ||
|
|
73f3e448cf | ||
|
|
58a17897cf | ||
|
|
cf3106f636 | ||
|
|
a553a1b683 | ||
|
|
2a6bbd66e6 | ||
|
|
5a4baa05ca | ||
|
|
4ec9b9b475 | ||
|
|
1e2288b9a8 | ||
|
|
74f58a88bf | ||
|
|
69ee8752d0 | ||
|
|
0ef16bd55a | ||
|
|
27fa0afb72 | ||
|
|
c98c99e488 | ||
|
|
2d838d7699 | ||
|
|
d5bb036939 | ||
|
|
87ef469e25 | ||
|
|
72df652f6b | ||
|
|
c67675c138 | ||
|
|
e8d2b7eb3c | ||
|
|
83722f1a02 | ||
|
|
de42de9d62 | ||
|
|
bb3ae1ef70 | ||
|
|
5dfa94d76e | ||
|
|
dfe63f2318 | ||
|
|
026745ac8e | ||
|
|
9cf64a43f5 | ||
|
|
5e07924aca | ||
|
|
77078e78d1 | ||
|
|
bf2362d836 | ||
|
|
1c11523d9d | ||
|
|
150a87413d | ||
|
|
f7221a7355 | ||
|
|
4197ec198c | ||
|
|
5484b7c491 | ||
|
|
041223b558 | ||
|
|
71c04d20ef | ||
|
|
727d75bccc | ||
|
|
d987bb17d2 | ||
|
|
89946041a1 | ||
|
|
b919c93f28 | ||
|
|
eac751190d | ||
|
|
2bc2051a46 | ||
|
|
89ad4e0f3a | ||
|
|
72f4753620 | ||
|
|
4badaadcc1 | ||
|
|
3f01f20f0c | ||
|
|
1fbb00f8f0 | ||
|
|
da7d3590fc | ||
|
|
256006ca3e | ||
|
|
213528c619 | ||
|
|
8b47dba05d | ||
|
|
5e5d5de91a | ||
|
|
680ea71958 | ||
|
|
5fb5dbbbf5 | ||
|
|
b3fe448ff1 | ||
|
|
101a54e8da | ||
|
|
3308cab826 | ||
|
|
5fdd8288f4 | ||
|
|
4cb32b40e6 | ||
|
|
afa81c7ec2 | ||
|
|
e84c7d3310 | ||
|
|
7d0a90cb78 | ||
|
|
24f79922e9 | ||
|
|
c3995009ee | ||
|
|
6e9fe2986e | ||
|
|
603240fedb | ||
|
|
e61871a68e | ||
|
|
379af59f07 | ||
|
|
ef9afe31a4 | ||
|
|
dca636b0fd | ||
|
|
9b72cc7aa6 | ||
|
|
d3c023b3ba | ||
|
|
5f2a4deb19 | ||
|
|
91f290987e | ||
|
|
2f3215b71a | ||
|
|
2e87a01346 | ||
|
|
453003bf14 | ||
|
|
80ca377668 | ||
|
|
d21297bc9c |
10
.github/workflows/acceptance_tests.yml
vendored
10
.github/workflows/acceptance_tests.yml
vendored
@@ -24,6 +24,16 @@ jobs:
|
|||||||
- name: Setup acceptance test
|
- name: Setup acceptance test
|
||||||
run: source ./acceptanceTests/setup.sh
|
run: source ./acceptanceTests/setup.sh
|
||||||
|
|
||||||
|
- name: Create k8s users and change context
|
||||||
|
env:
|
||||||
|
USERNAME_UNRESTRICTED: user-with-clusterwide-access
|
||||||
|
USERNAME_RESTRICTED: user-with-restricted-access
|
||||||
|
run: |
|
||||||
|
./acceptanceTests/create_user.sh "${USERNAME_UNRESTRICTED}"
|
||||||
|
./acceptanceTests/create_user.sh "${USERNAME_RESTRICTED}"
|
||||||
|
kubectl apply -f cli/cmd/permissionFiles/permissions-all-namespaces-tap.yaml
|
||||||
|
kubectl config use-context ${USERNAME_UNRESTRICTED}
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: make acceptance-test
|
run: make acceptance-test
|
||||||
|
|
||||||
|
|||||||
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
@@ -58,6 +58,7 @@ jobs:
|
|||||||
up9inc/mizu
|
up9inc/mizu
|
||||||
tags: |
|
tags: |
|
||||||
type=raw,${{ steps.versioning.outputs.version }}
|
type=raw,${{ steps.versioning.outputs.version }}
|
||||||
|
type=raw,value=latest,enable=${{ steps.condval.outputs.value == 'stable' }}
|
||||||
flavor: |
|
flavor: |
|
||||||
latest=auto
|
latest=auto
|
||||||
prefix=
|
prefix=
|
||||||
@@ -143,6 +144,7 @@ jobs:
|
|||||||
${{ steps.base_image_step.outputs.image }}
|
${{ steps.base_image_step.outputs.image }}
|
||||||
tags: |
|
tags: |
|
||||||
type=raw,${{ steps.versioning.outputs.version }}
|
type=raw,${{ steps.versioning.outputs.version }}
|
||||||
|
type=raw,value=latest,enable=${{ steps.condval.outputs.value == 'stable' }}
|
||||||
flavor: |
|
flavor: |
|
||||||
latest=auto
|
latest=auto
|
||||||
prefix=
|
prefix=
|
||||||
@@ -205,6 +207,7 @@ jobs:
|
|||||||
up9inc/mizu
|
up9inc/mizu
|
||||||
tags: |
|
tags: |
|
||||||
type=raw,${{ steps.versioning.outputs.version }}
|
type=raw,${{ steps.versioning.outputs.version }}
|
||||||
|
type=raw,value=latest,enable=${{ steps.condval.outputs.value == 'stable' }}
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v1
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ RUN go build -ldflags="-extldflags=-static -s -w \
|
|||||||
-X 'github.com/up9inc/mizu/agent/pkg/version.Ver=${VER}'" -o mizuagent .
|
-X 'github.com/up9inc/mizu/agent/pkg/version.Ver=${VER}'" -o mizuagent .
|
||||||
|
|
||||||
# Download Basenine executable, verify the sha1sum
|
# Download Basenine executable, verify the sha1sum
|
||||||
ADD https://github.com/up9inc/basenine/releases/download/v0.4.16/basenine_linux_${GOARCH} ./basenine_linux_${GOARCH}
|
ADD https://github.com/up9inc/basenine/releases/download/v0.4.17/basenine_linux_${GOARCH} ./basenine_linux_${GOARCH}
|
||||||
ADD https://github.com/up9inc/basenine/releases/download/v0.4.16/basenine_linux_${GOARCH}.sha256 ./basenine_linux_${GOARCH}.sha256
|
ADD https://github.com/up9inc/basenine/releases/download/v0.4.17/basenine_linux_${GOARCH}.sha256 ./basenine_linux_${GOARCH}.sha256
|
||||||
RUN shasum -a 256 -c basenine_linux_${GOARCH}.sha256
|
RUN shasum -a 256 -c basenine_linux_${GOARCH}.sha256
|
||||||
RUN chmod +x ./basenine_linux_${GOARCH}
|
RUN chmod +x ./basenine_linux_${GOARCH}
|
||||||
RUN mv ./basenine_linux_${GOARCH} ./basenine
|
RUN mv ./basenine_linux_${GOARCH} ./basenine
|
||||||
|
|||||||
3
Makefile
3
Makefile
@@ -103,6 +103,9 @@ test-shared: ## Run shared tests
|
|||||||
|
|
||||||
test-extensions: ## Run extensions tests
|
test-extensions: ## Run extensions tests
|
||||||
@echo "running http tests"; cd tap/extensions/http && $(MAKE) test
|
@echo "running http tests"; cd tap/extensions/http && $(MAKE) test
|
||||||
|
@echo "running redis tests"; cd tap/extensions/redis && $(MAKE) test
|
||||||
|
@echo "running kafka tests"; cd tap/extensions/kafka && $(MAKE) test
|
||||||
|
@echo "running amqp tests"; cd tap/extensions/amqp && $(MAKE) test
|
||||||
|
|
||||||
acceptance-test: ## Run acceptance tests
|
acceptance-test: ## Run acceptance tests
|
||||||
@echo "running acceptance tests"; cd acceptanceTests && $(MAKE) test
|
@echo "running acceptance tests"; cd acceptanceTests && $(MAKE) test
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
<img alt="GitHub Latest Release" src="https://img.shields.io/github/v/release/up9inc/mizu?logo=GitHub&style=flat-square">
|
<img alt="GitHub Latest Release" src="https://img.shields.io/github/v/release/up9inc/mizu?logo=GitHub&style=flat-square">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://hub.docker.com/r/up9inc/mizu">
|
<a href="https://hub.docker.com/r/up9inc/mizu">
|
||||||
<img alt="Docker pulls" src="https://img.shields.io/docker/pulls/up9inc/mizu?color=%23099cec">
|
<img alt="Docker pulls" src="https://img.shields.io/docker/pulls/up9inc/mizu?color=%23099cec&logo=Docker&style=flat-square">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://hub.docker.com/r/up9inc/mizu">
|
<a href="https://hub.docker.com/r/up9inc/mizu">
|
||||||
<img alt="Image size" src="https://img.shields.io/docker/image-size/up9inc/mizu/latest">
|
<img alt="Image size" src="https://img.shields.io/docker/image-size/up9inc/mizu/latest?logo=Docker&style=flat-square">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://join.slack.com/t/up9/shared_invite/zt-tfjnduli-QzlR8VV4Z1w3YnPIAJfhlQ">
|
<a href="https://join.slack.com/t/up9/shared_invite/zt-tfjnduli-QzlR8VV4Z1w3YnPIAJfhlQ">
|
||||||
<img alt="Slack" src="https://img.shields.io/badge/slack-join_chat-white.svg?logo=slack&style=social">
|
<img alt="Slack" src="https://img.shields.io/badge/slack-join_chat-white.svg?logo=slack&style=social">
|
||||||
|
|||||||
@@ -23,19 +23,19 @@ func TestConfigRegenerate(t *testing.T) {
|
|||||||
t.Skip("ignored acceptance test")
|
t.Skip("ignored acceptance test")
|
||||||
}
|
}
|
||||||
|
|
||||||
cliPath, cliPathErr := getCliPath()
|
cliPath, cliPathErr := GetCliPath()
|
||||||
if cliPathErr != nil {
|
if cliPathErr != nil {
|
||||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
configPath, configPathErr := getConfigPath()
|
configPath, configPathErr := GetConfigPath()
|
||||||
if configPathErr != nil {
|
if configPathErr != nil {
|
||||||
t.Errorf("failed to get config path, err: %v", cliPathErr)
|
t.Errorf("failed to get config path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
configCmdArgs := getDefaultConfigCommandArgs()
|
configCmdArgs := GetDefaultConfigCommandArgs()
|
||||||
|
|
||||||
configCmdArgs = append(configCmdArgs, "-r")
|
configCmdArgs = append(configCmdArgs, "-r")
|
||||||
|
|
||||||
@@ -74,13 +74,13 @@ func TestConfigGuiPort(t *testing.T) {
|
|||||||
|
|
||||||
for _, guiPort := range tests {
|
for _, guiPort := range tests {
|
||||||
t.Run(fmt.Sprintf("%d", guiPort), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%d", guiPort), func(t *testing.T) {
|
||||||
cliPath, cliPathErr := getCliPath()
|
cliPath, cliPathErr := GetCliPath()
|
||||||
if cliPathErr != nil {
|
if cliPathErr != nil {
|
||||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
configPath, configPathErr := getConfigPath()
|
configPath, configPathErr := GetConfigPath()
|
||||||
if configPathErr != nil {
|
if configPathErr != nil {
|
||||||
t.Errorf("failed to get config path, err: %v", cliPathErr)
|
t.Errorf("failed to get config path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
@@ -100,16 +100,16 @@ func TestConfigGuiPort(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tapCmdArgs := getDefaultTapCommandArgs()
|
tapCmdArgs := GetDefaultTapCommandArgs()
|
||||||
|
|
||||||
tapNamespace := getDefaultTapNamespace()
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
||||||
t.Logf("running command: %v", tapCmd.String())
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
if err := cleanupCommand(tapCmd); err != nil {
|
if err := CleanupCommand(tapCmd); err != nil {
|
||||||
t.Logf("failed to cleanup tap command, err: %v", err)
|
t.Logf("failed to cleanup tap command, err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,9 +123,9 @@ func TestConfigGuiPort(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiServerUrl := getApiServerUrl(guiPort)
|
apiServerUrl := GetApiServerUrl(guiPort)
|
||||||
|
|
||||||
if err := waitTapPodsReady(apiServerUrl); err != nil {
|
if err := WaitTapPodsReady(apiServerUrl); err != nil {
|
||||||
t.Errorf("failed to start tap pods on time, err: %v", err)
|
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -147,13 +147,13 @@ func TestConfigSetGuiPort(t *testing.T) {
|
|||||||
|
|
||||||
for _, guiPortStruct := range tests {
|
for _, guiPortStruct := range tests {
|
||||||
t.Run(fmt.Sprintf("%d", guiPortStruct.SetGuiPort), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%d", guiPortStruct.SetGuiPort), func(t *testing.T) {
|
||||||
cliPath, cliPathErr := getCliPath()
|
cliPath, cliPathErr := GetCliPath()
|
||||||
if cliPathErr != nil {
|
if cliPathErr != nil {
|
||||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
configPath, configPathErr := getConfigPath()
|
configPath, configPathErr := GetConfigPath()
|
||||||
if configPathErr != nil {
|
if configPathErr != nil {
|
||||||
t.Errorf("failed to get config path, err: %v", cliPathErr)
|
t.Errorf("failed to get config path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
@@ -173,9 +173,9 @@ func TestConfigSetGuiPort(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tapCmdArgs := getDefaultTapCommandArgs()
|
tapCmdArgs := GetDefaultTapCommandArgs()
|
||||||
|
|
||||||
tapNamespace := getDefaultTapNamespace()
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
tapCmdArgs = append(tapCmdArgs, "--set", fmt.Sprintf("tap.gui-port=%v", guiPortStruct.SetGuiPort))
|
tapCmdArgs = append(tapCmdArgs, "--set", fmt.Sprintf("tap.gui-port=%v", guiPortStruct.SetGuiPort))
|
||||||
@@ -184,7 +184,7 @@ func TestConfigSetGuiPort(t *testing.T) {
|
|||||||
t.Logf("running command: %v", tapCmd.String())
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
if err := cleanupCommand(tapCmd); err != nil {
|
if err := CleanupCommand(tapCmd); err != nil {
|
||||||
t.Logf("failed to cleanup tap command, err: %v", err)
|
t.Logf("failed to cleanup tap command, err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,9 +198,9 @@ func TestConfigSetGuiPort(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiServerUrl := getApiServerUrl(guiPortStruct.SetGuiPort)
|
apiServerUrl := GetApiServerUrl(guiPortStruct.SetGuiPort)
|
||||||
|
|
||||||
if err := waitTapPodsReady(apiServerUrl); err != nil {
|
if err := WaitTapPodsReady(apiServerUrl); err != nil {
|
||||||
t.Errorf("failed to start tap pods on time, err: %v", err)
|
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -222,13 +222,13 @@ func TestConfigFlagGuiPort(t *testing.T) {
|
|||||||
|
|
||||||
for _, guiPortStruct := range tests {
|
for _, guiPortStruct := range tests {
|
||||||
t.Run(fmt.Sprintf("%d", guiPortStruct.FlagGuiPort), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%d", guiPortStruct.FlagGuiPort), func(t *testing.T) {
|
||||||
cliPath, cliPathErr := getCliPath()
|
cliPath, cliPathErr := GetCliPath()
|
||||||
if cliPathErr != nil {
|
if cliPathErr != nil {
|
||||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
configPath, configPathErr := getConfigPath()
|
configPath, configPathErr := GetConfigPath()
|
||||||
if configPathErr != nil {
|
if configPathErr != nil {
|
||||||
t.Errorf("failed to get config path, err: %v", cliPathErr)
|
t.Errorf("failed to get config path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
@@ -248,9 +248,9 @@ func TestConfigFlagGuiPort(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tapCmdArgs := getDefaultTapCommandArgs()
|
tapCmdArgs := GetDefaultTapCommandArgs()
|
||||||
|
|
||||||
tapNamespace := getDefaultTapNamespace()
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
tapCmdArgs = append(tapCmdArgs, "-p", fmt.Sprintf("%v", guiPortStruct.FlagGuiPort))
|
tapCmdArgs = append(tapCmdArgs, "-p", fmt.Sprintf("%v", guiPortStruct.FlagGuiPort))
|
||||||
@@ -259,7 +259,7 @@ func TestConfigFlagGuiPort(t *testing.T) {
|
|||||||
t.Logf("running command: %v", tapCmd.String())
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
if err := cleanupCommand(tapCmd); err != nil {
|
if err := CleanupCommand(tapCmd); err != nil {
|
||||||
t.Logf("failed to cleanup tap command, err: %v", err)
|
t.Logf("failed to cleanup tap command, err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,9 +273,9 @@ func TestConfigFlagGuiPort(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiServerUrl := getApiServerUrl(guiPortStruct.FlagGuiPort)
|
apiServerUrl := GetApiServerUrl(guiPortStruct.FlagGuiPort)
|
||||||
|
|
||||||
if err := waitTapPodsReady(apiServerUrl); err != nil {
|
if err := WaitTapPodsReady(apiServerUrl); err != nil {
|
||||||
t.Errorf("failed to start tap pods on time, err: %v", err)
|
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
37
acceptanceTests/create_user.sh
Executable file
37
acceptanceTests/create_user.sh
Executable file
@@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Create a user in Minikube cluster "minikube"
|
||||||
|
# Create context for user
|
||||||
|
# Usage:
|
||||||
|
# ./create_user.sh <username>
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
NEW_USERNAME=$1
|
||||||
|
CERT_DIR="${HOME}/certs"
|
||||||
|
KEY_FILE="${CERT_DIR}/${NEW_USERNAME}.key"
|
||||||
|
CRT_FILE="${CERT_DIR}/${NEW_USERNAME}.crt"
|
||||||
|
MINIKUBE_KEY_FILE="${HOME}/.minikube/ca.key"
|
||||||
|
MINIKUBE_CRT_FILE="${HOME}/.minikube/ca.crt"
|
||||||
|
DAYS=1
|
||||||
|
|
||||||
|
echo "Creating user and context for username \"${NEW_USERNAME}\" in Minikube cluster"
|
||||||
|
|
||||||
|
if ! command -v openssl &> /dev/null
|
||||||
|
then
|
||||||
|
echo "Installing openssl"
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install openssl
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Creating certificate for user \"${NEW_USERNAME}\""
|
||||||
|
mkdir -p ${CERT_DIR}
|
||||||
|
echo "Generating key \"${KEY_FILE}\""
|
||||||
|
openssl genrsa -out "${KEY_FILE}" 2048
|
||||||
|
echo "Generating crt \"${CRT_FILE}\""
|
||||||
|
openssl req -new -key "${KEY_FILE}" -out "${CRT_FILE}" -subj "/CN=${NEW_USERNAME}/O=group1"
|
||||||
|
openssl x509 -req -in "${CRT_FILE}" -CA "${MINIKUBE_CRT_FILE}" -CAkey "${MINIKUBE_KEY_FILE}" -CAcreateserial -out "${CRT_FILE}" -days $DAYS
|
||||||
|
|
||||||
|
echo "Creating context for user \"${NEW_USERNAME}\""
|
||||||
|
kubectl config set-credentials "${NEW_USERNAME}" --client-certificate="${CRT_FILE}" --client-key="${KEY_FILE}"
|
||||||
|
kubectl config set-context "${NEW_USERNAME}" --cluster=minikube --user="${NEW_USERNAME}"
|
||||||
@@ -14,7 +14,9 @@
|
|||||||
"tests/RegexMasking.js",
|
"tests/RegexMasking.js",
|
||||||
"tests/IgnoredUserAgents.js",
|
"tests/IgnoredUserAgents.js",
|
||||||
"tests/UiTest.js",
|
"tests/UiTest.js",
|
||||||
"tests/Redis.js"
|
"tests/Redis.js",
|
||||||
|
"tests/Rabbit.js",
|
||||||
|
"tests/serviceMapFunction.js"
|
||||||
],
|
],
|
||||||
|
|
||||||
"env": {
|
"env": {
|
||||||
@@ -22,9 +24,11 @@
|
|||||||
"redactHeaderContent": "User-Header[REDACTED]",
|
"redactHeaderContent": "User-Header[REDACTED]",
|
||||||
"redactBodyContent": "{ \"User\": \"[REDACTED]\" }",
|
"redactBodyContent": "{ \"User\": \"[REDACTED]\" }",
|
||||||
"regexMaskingBodyContent": "[REDACTED]",
|
"regexMaskingBodyContent": "[REDACTED]",
|
||||||
"minimumEntries": 25,
|
|
||||||
"greenFilterColor": "rgb(210, 250, 210)",
|
"greenFilterColor": "rgb(210, 250, 210)",
|
||||||
"redFilterColor": "rgb(250, 214, 220)",
|
"redFilterColor": "rgb(250, 214, 220)",
|
||||||
"bodyJsonClass": ".hljs"
|
"bodyJsonClass": ".hljs",
|
||||||
|
"mizuWidth": 1920,
|
||||||
|
"normalMizuHeight": 1080,
|
||||||
|
"hugeMizuHeight": 3500
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
export const valueTabs = {
|
||||||
|
response: 'RESPONSE',
|
||||||
|
request: 'REQUEST',
|
||||||
|
none: null
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxEntriesInDom = 13;
|
||||||
|
|
||||||
export function isValueExistsInElement(shouldInclude, content, domPathToContainer){
|
export function isValueExistsInElement(shouldInclude, content, domPathToContainer){
|
||||||
it(`should ${shouldInclude ? '' : 'not'} include '${content}'`, function () {
|
it(`should ${shouldInclude ? '' : 'not'} include '${content}'`, function () {
|
||||||
cy.get(domPathToContainer).then(htmlText => {
|
cy.get(domPathToContainer).then(htmlText => {
|
||||||
@@ -9,22 +17,24 @@ export function isValueExistsInElement(shouldInclude, content, domPathToContaine
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function resizeToHugeMizu() {
|
export function resizeToHugeMizu() {
|
||||||
cy.viewport(1920, 3500);
|
cy.viewport(Cypress.env('mizuWidth'), Cypress.env('hugeMizuHeight'));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resizeToNormalMizu() {
|
export function resizeToNormalMizu() {
|
||||||
cy.viewport(1920, 1080);
|
cy.viewport(Cypress.env('mizuWidth'), Cypress.env('normalMizuHeight'));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function verifyMinimumEntries() {
|
export function verifyMinimumEntries() {
|
||||||
const minimumEntries = Cypress.env('minimumEntries');
|
const entriesSent = Cypress.env('entriesCount');
|
||||||
it(`Making sure that mizu shows at least ${minimumEntries} entries`, async function () {
|
const minimumEntries = Math.round((0.75 * entriesSent));
|
||||||
|
|
||||||
|
it(`Making sure that mizu shows at least ${minimumEntries} entries`, function () {
|
||||||
cy.get('#total-entries').then(number => {
|
cy.get('#total-entries').then(number => {
|
||||||
const getNum = () => {
|
const getNum = () => {
|
||||||
const numOfEntries = number.text();
|
return parseInt(number.text());
|
||||||
return parseInt(numOfEntries);
|
|
||||||
};
|
};
|
||||||
cy.wrap({there: getNum}).invoke('there').should('be.gte', minimumEntries);
|
|
||||||
|
cy.wrap({num: getNum}).invoke('num').should('be.gt', minimumEntries);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -53,3 +63,125 @@ export function checkThatAllEntriesShown() {
|
|||||||
cy.get('[title="Fetch old records"]').click();
|
cy.get('[title="Fetch old records"]').click();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function checkFilterByMethod(funcDict) {
|
||||||
|
const {protocol, method, summary, hugeMizu} = funcDict;
|
||||||
|
const summaryDict = getSummeryDict(summary);
|
||||||
|
const methodDict = getMethodDict(method);
|
||||||
|
const protocolDict = getProtocolDict(protocol.name, protocol.text);
|
||||||
|
|
||||||
|
it(`Testing the method: ${method}`, function () {
|
||||||
|
// applying filter
|
||||||
|
cy.get('.w-tc-editor-text').clear().type(`method == "${method}"`);
|
||||||
|
cy.get('[type="submit"]').click();
|
||||||
|
cy.get('.w-tc-editor').should('have.attr', 'style').and('include', Cypress.env('greenFilterColor'));
|
||||||
|
|
||||||
|
cy.get('#entries-length').then(number => {
|
||||||
|
// if the entries list isn't expanded it expands here
|
||||||
|
if (number.text() === '0' || number.text() === '1') // todo change when TRA-4262 is fixed
|
||||||
|
cy.get('[title="Fetch old records"]').click();
|
||||||
|
|
||||||
|
cy.get('#entries-length').should('not.have.text', '0').and('not.have.text', '1').then(() => {
|
||||||
|
cy.get(`#list [id]`).then(elements => {
|
||||||
|
const listElmWithIdAttr = Object.values(elements);
|
||||||
|
let doneCheckOnFirst = false;
|
||||||
|
|
||||||
|
cy.get('#entries-length').invoke('text').then(len => {
|
||||||
|
resizeIfNeeded(len);
|
||||||
|
listElmWithIdAttr.forEach(entry => {
|
||||||
|
if (entry?.id && entry.id.match(RegExp(/entry-(\d{2}|\d{1})$/gm))) {
|
||||||
|
const entryNum = getEntryNumById(entry.id);
|
||||||
|
|
||||||
|
leftTextCheck(entryNum, methodDict.pathLeft, methodDict.expectedText);
|
||||||
|
leftTextCheck(entryNum, protocolDict.pathLeft, protocolDict.expectedTextLeft);
|
||||||
|
if (summaryDict)
|
||||||
|
leftTextCheck(entryNum, summaryDict.pathLeft, summaryDict.expectedText);
|
||||||
|
|
||||||
|
if (!doneCheckOnFirst) {
|
||||||
|
deepCheck(funcDict, protocolDict, methodDict, entry);
|
||||||
|
doneCheckOnFirst = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resizeIfNeeded(len);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resizeIfNeeded(entriesLen) {
|
||||||
|
if (entriesLen > maxEntriesInDom){
|
||||||
|
Cypress.config().viewportHeight === Cypress.env('normalMizuHeight') ?
|
||||||
|
resizeToHugeMizu() : resizeToNormalMizu()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deepCheck(generalDict, protocolDict, methodDict, entry) {
|
||||||
|
const entryNum = getEntryNumById(entry.id);
|
||||||
|
const {summary, value} = generalDict;
|
||||||
|
const summaryDict = getSummeryDict(summary);
|
||||||
|
|
||||||
|
leftOnHoverCheck(entryNum, methodDict.pathLeft, methodDict.expectedOnHover);
|
||||||
|
leftOnHoverCheck(entryNum, protocolDict.pathLeft, protocolDict.expectedOnHover);
|
||||||
|
if (summaryDict)
|
||||||
|
leftOnHoverCheck(entryNum, summaryDict.pathLeft, summaryDict.expectedOnHover);
|
||||||
|
|
||||||
|
cy.get(`#${entry.id}`).click();
|
||||||
|
|
||||||
|
rightTextCheck(methodDict.pathRight, methodDict.expectedText);
|
||||||
|
rightTextCheck(protocolDict.pathRight, protocolDict.expectedTextRight);
|
||||||
|
if (summaryDict)
|
||||||
|
rightTextCheck(summaryDict.pathRight, summaryDict.expectedText);
|
||||||
|
|
||||||
|
rightOnHoverCheck(methodDict.pathRight, methodDict.expectedOnHover);
|
||||||
|
rightOnHoverCheck(protocolDict.pathRight, protocolDict.expectedOnHover);
|
||||||
|
if (summaryDict)
|
||||||
|
rightOnHoverCheck(summaryDict.pathRight, summaryDict.expectedOnHover);
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
if (value.tab === valueTabs.response)
|
||||||
|
cy.contains('Response').click();
|
||||||
|
cy.get(Cypress.env('bodyJsonClass')).then(text => {
|
||||||
|
expect(text.text()).to.match(value.regex)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSummeryDict(summary) {
|
||||||
|
if (summary) {
|
||||||
|
return {
|
||||||
|
pathLeft: '> :nth-child(2) > :nth-child(1) > :nth-child(2) > :nth-child(2)',
|
||||||
|
pathRight: '> :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(2) > :nth-child(2)',
|
||||||
|
expectedText: summary,
|
||||||
|
expectedOnHover: `summary == "${summary}"`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMethodDict(method) {
|
||||||
|
return {
|
||||||
|
pathLeft: '> :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(2)',
|
||||||
|
pathRight: '> :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(1) > :nth-child(2)',
|
||||||
|
expectedText: method,
|
||||||
|
expectedOnHover: `method == "${method}"`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProtocolDict(protocol, protocolText) {
|
||||||
|
return {
|
||||||
|
pathLeft: '> :nth-child(1) > :nth-child(1)',
|
||||||
|
pathRight: '> :nth-child(1) > :nth-child(1) > :nth-child(1) > :nth-child(1)',
|
||||||
|
expectedTextLeft: protocol.toUpperCase(),
|
||||||
|
expectedTextRight: protocolText,
|
||||||
|
expectedOnHover: protocol.toLowerCase()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEntryNumById (id) {
|
||||||
|
return parseInt(id.split('-')[1]);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import {findLineAndCheck, getExpectedDetailsDict} from "../testHelpers/StatusBarHelper";
|
import {findLineAndCheck, getExpectedDetailsDict} from "../testHelpers/StatusBarHelper";
|
||||||
import {verifyMinimumEntries} from "../testHelpers/TrafficHelper";
|
|
||||||
|
|
||||||
it('check', function () {
|
it('check', function () {
|
||||||
const podName = Cypress.env('name'), namespace = Cypress.env('namespace');
|
const podName = Cypress.env('name'), namespace = Cypress.env('namespace');
|
||||||
@@ -9,8 +8,6 @@ it('check', function () {
|
|||||||
cy.visit(`http://localhost:${port}`);
|
cy.visit(`http://localhost:${port}`);
|
||||||
cy.wait('@statusTap').its('response.statusCode').should('match', /^2\d{2}/);
|
cy.wait('@statusTap').its('response.statusCode').should('match', /^2\d{2}/);
|
||||||
|
|
||||||
verifyMinimumEntries();
|
|
||||||
|
|
||||||
cy.get('.podsCount').trigger('mouseover');
|
cy.get('.podsCount').trigger('mouseover');
|
||||||
findLineAndCheck(getExpectedDetailsDict(podName, namespace));
|
findLineAndCheck(getExpectedDetailsDict(podName, namespace));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,15 +2,12 @@ import {
|
|||||||
checkThatAllEntriesShown,
|
checkThatAllEntriesShown,
|
||||||
isValueExistsInElement,
|
isValueExistsInElement,
|
||||||
resizeToHugeMizu,
|
resizeToHugeMizu,
|
||||||
verifyMinimumEntries
|
|
||||||
} from "../testHelpers/TrafficHelper";
|
} from "../testHelpers/TrafficHelper";
|
||||||
|
|
||||||
it('Loading Mizu', function () {
|
it('Loading Mizu', function () {
|
||||||
cy.visit(Cypress.env('testUrl'));
|
cy.visit(Cypress.env('testUrl'));
|
||||||
});
|
});
|
||||||
|
|
||||||
verifyMinimumEntries();
|
|
||||||
|
|
||||||
checkEntries();
|
checkEntries();
|
||||||
|
|
||||||
function checkEntries() {
|
function checkEntries() {
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import {isValueExistsInElement, verifyMinimumEntries} from '../testHelpers/TrafficHelper';
|
import {isValueExistsInElement} from '../testHelpers/TrafficHelper';
|
||||||
|
|
||||||
it('Loading Mizu', function () {
|
it('Loading Mizu', function () {
|
||||||
cy.visit(Cypress.env('testUrl'));
|
cy.visit(Cypress.env('testUrl'));
|
||||||
});
|
});
|
||||||
|
|
||||||
verifyMinimumEntries();
|
|
||||||
|
|
||||||
isValueExistsInElement(false, Cypress.env('redactHeaderContent'), '#tbody-Headers');
|
isValueExistsInElement(false, Cypress.env('redactHeaderContent'), '#tbody-Headers');
|
||||||
isValueExistsInElement(false, Cypress.env('redactBodyContent'), Cypress.env('bodyJsonClass'));
|
isValueExistsInElement(false, Cypress.env('redactBodyContent'), Cypress.env('bodyJsonClass'));
|
||||||
|
|||||||
49
acceptanceTests/cypress/integration/tests/Rabbit.js
Normal file
49
acceptanceTests/cypress/integration/tests/Rabbit.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import {checkFilterByMethod, valueTabs,} from "../testHelpers/TrafficHelper";
|
||||||
|
|
||||||
|
it('opening mizu', function () {
|
||||||
|
cy.visit(Cypress.env('testUrl'));
|
||||||
|
});
|
||||||
|
|
||||||
|
const rabbitProtocolDetails = {name: 'AMQP', text: 'Advanced Message Queuing Protocol 0-9-1'};
|
||||||
|
|
||||||
|
checkFilterByMethod({
|
||||||
|
protocol: rabbitProtocolDetails,
|
||||||
|
method: 'exchange declare',
|
||||||
|
summary: 'exchange',
|
||||||
|
value: null
|
||||||
|
});
|
||||||
|
|
||||||
|
checkFilterByMethod({
|
||||||
|
protocol: rabbitProtocolDetails,
|
||||||
|
method: 'queue declare',
|
||||||
|
summary: 'queue',
|
||||||
|
value: null
|
||||||
|
});
|
||||||
|
|
||||||
|
checkFilterByMethod({
|
||||||
|
protocol: rabbitProtocolDetails,
|
||||||
|
method: 'queue bind',
|
||||||
|
summary: 'queue',
|
||||||
|
value: null
|
||||||
|
});
|
||||||
|
|
||||||
|
checkFilterByMethod({
|
||||||
|
protocol: rabbitProtocolDetails,
|
||||||
|
method: 'basic publish',
|
||||||
|
summary: 'exchange',
|
||||||
|
value: {tab: valueTabs.request, regex: /^message$/mg}
|
||||||
|
});
|
||||||
|
|
||||||
|
checkFilterByMethod({
|
||||||
|
protocol: rabbitProtocolDetails,
|
||||||
|
method: 'basic consume',
|
||||||
|
summary: 'queue',
|
||||||
|
value: null
|
||||||
|
});
|
||||||
|
|
||||||
|
checkFilterByMethod({
|
||||||
|
protocol: rabbitProtocolDetails,
|
||||||
|
method: 'basic deliver',
|
||||||
|
summary: 'exchange',
|
||||||
|
value: {tab: valueTabs.request, regex: /^message$/mg}
|
||||||
|
});
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
import {isValueExistsInElement, verifyMinimumEntries} from '../testHelpers/TrafficHelper';
|
import {isValueExistsInElement} from '../testHelpers/TrafficHelper';
|
||||||
|
|
||||||
it('Loading Mizu', function () {
|
it('Loading Mizu', function () {
|
||||||
cy.visit(Cypress.env('testUrl'));
|
cy.visit(Cypress.env('testUrl'));
|
||||||
});
|
});
|
||||||
|
|
||||||
verifyMinimumEntries();
|
|
||||||
|
|
||||||
isValueExistsInElement(true, Cypress.env('redactHeaderContent'), '#tbody-Headers');
|
isValueExistsInElement(true, Cypress.env('redactHeaderContent'), '#tbody-Headers');
|
||||||
isValueExistsInElement(true, Cypress.env('redactBodyContent'), Cypress.env('bodyJsonClass'));
|
isValueExistsInElement(true, Cypress.env('redactBodyContent'), Cypress.env('bodyJsonClass'));
|
||||||
|
|||||||
@@ -1,155 +1,42 @@
|
|||||||
import {
|
import {checkFilterByMethod, valueTabs,} from "../testHelpers/TrafficHelper";
|
||||||
leftOnHoverCheck,
|
|
||||||
leftTextCheck,
|
|
||||||
rightOnHoverCheck,
|
|
||||||
rightTextCheck,
|
|
||||||
} from "../testHelpers/TrafficHelper";
|
|
||||||
|
|
||||||
const valueTabs = {
|
|
||||||
response: 'RESPONSE',
|
|
||||||
request: 'REQUEST',
|
|
||||||
none: null
|
|
||||||
}
|
|
||||||
|
|
||||||
it('opening mizu', function () {
|
it('opening mizu', function () {
|
||||||
cy.visit(Cypress.env('testUrl'));
|
cy.visit(Cypress.env('testUrl'));
|
||||||
});
|
});
|
||||||
|
|
||||||
checkRedisFilterByMethod({
|
const redisProtocolDetails = {name: 'redis', text: 'Redis Serialization Protocol'};
|
||||||
|
|
||||||
|
checkFilterByMethod({
|
||||||
|
protocol: redisProtocolDetails,
|
||||||
method: 'PING',
|
method: 'PING',
|
||||||
shouldCheckSummary: false,
|
summary: null,
|
||||||
valueTab: valueTabs.none
|
value: null
|
||||||
});
|
})
|
||||||
|
|
||||||
checkRedisFilterByMethod({
|
checkFilterByMethod({
|
||||||
|
protocol: redisProtocolDetails,
|
||||||
method: 'SET',
|
method: 'SET',
|
||||||
shouldCheckSummary: true,
|
summary: 'key',
|
||||||
valueTab: valueTabs.request,
|
value: {tab: valueTabs.request, regex: /^\[value, keepttl]$/mg}
|
||||||
valueRegex: /^\[value, keepttl]$/mg
|
})
|
||||||
});
|
|
||||||
|
|
||||||
checkRedisFilterByMethod({
|
checkFilterByMethod({
|
||||||
|
protocol: redisProtocolDetails,
|
||||||
method: 'EXISTS',
|
method: 'EXISTS',
|
||||||
shouldCheckSummary: true,
|
summary: 'key',
|
||||||
valueTab: valueTabs.response,
|
value: {tab: valueTabs.response, regex: /^1$/mg}
|
||||||
valueRegex: /^1$/mg
|
})
|
||||||
});
|
|
||||||
|
|
||||||
checkRedisFilterByMethod({
|
checkFilterByMethod({
|
||||||
|
protocol: redisProtocolDetails,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
shouldCheckSummary: true,
|
summary: 'key',
|
||||||
valueTab: valueTabs.response,
|
value: {tab: valueTabs.response, regex: /^value$/mg}
|
||||||
valueRegex: /^value$/mg
|
})
|
||||||
});
|
|
||||||
|
|
||||||
checkRedisFilterByMethod({
|
checkFilterByMethod({
|
||||||
|
protocol: redisProtocolDetails,
|
||||||
method: 'DEL',
|
method: 'DEL',
|
||||||
shouldCheckSummary: true,
|
summary: 'key',
|
||||||
valueTab: valueTabs.response,
|
value: {tab: valueTabs.response, regex: /^1$|^0$/mg}
|
||||||
valueRegex: /^1$|^0$/mg
|
})
|
||||||
});
|
|
||||||
|
|
||||||
function checkRedisFilterByMethod(funcDict) {
|
|
||||||
const {method, shouldCheckSummary} = funcDict
|
|
||||||
const summaryDict = getSummeryDict();
|
|
||||||
const methodDict = getMethodDict(method);
|
|
||||||
const protocolDict = getProtocolDict();
|
|
||||||
|
|
||||||
it(`Testing the method: ${method}`, function () {
|
|
||||||
// applying filter
|
|
||||||
cy.get('.w-tc-editor-text').clear().type(`method == "${method}"`);
|
|
||||||
cy.get('[type="submit"]').click();
|
|
||||||
cy.get('.w-tc-editor').should('have.attr', 'style').and('include', Cypress.env('greenFilterColor'));
|
|
||||||
|
|
||||||
cy.get('#entries-length').then(number => {
|
|
||||||
// if the entries list isn't expanded it expands here
|
|
||||||
if (number.text() === '0' || number.text() === '1') // todo change when TRA-4262 is fixed
|
|
||||||
cy.get('[title="Fetch old records"]').click();
|
|
||||||
|
|
||||||
cy.get('#entries-length').should('not.have.text', '0').and('not.have.text', '1').then(() => {
|
|
||||||
cy.get(`#list [id]`).then(elements => {
|
|
||||||
const listElmWithIdAttr = Object.values(elements);
|
|
||||||
let doneCheckOnFirst = false;
|
|
||||||
|
|
||||||
listElmWithIdAttr.forEach(entry => {
|
|
||||||
if (entry?.id && entry.id.match(RegExp(/entry-(\d{2}|\d{1})$/gm))) {
|
|
||||||
const entryNum = getEntryNumById(entry.id);
|
|
||||||
|
|
||||||
leftTextCheck(entryNum, methodDict.pathLeft, methodDict.expectedText);
|
|
||||||
leftTextCheck(entryNum, protocolDict.pathLeft, protocolDict.expectedTextLeft);
|
|
||||||
if (shouldCheckSummary)
|
|
||||||
leftTextCheck(entryNum, summaryDict.pathLeft, summaryDict.expectedText);
|
|
||||||
|
|
||||||
if (!doneCheckOnFirst) {
|
|
||||||
deepCheck(funcDict, protocolDict, methodDict, summaryDict, entry);
|
|
||||||
doneCheckOnFirst = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function deepCheck(generalDict, protocolDict, methodDict, summaryDict, entry) {
|
|
||||||
const entryNum = getEntryNumById(entry.id);
|
|
||||||
const {shouldCheckSummary, valueTab, valueRegex} = generalDict;
|
|
||||||
|
|
||||||
leftOnHoverCheck(entryNum, methodDict.pathLeft, methodDict.expectedOnHover);
|
|
||||||
leftOnHoverCheck(entryNum, protocolDict.pathLeft, protocolDict.expectedOnHover);
|
|
||||||
if (shouldCheckSummary)
|
|
||||||
leftOnHoverCheck(entryNum, summaryDict.pathLeft, summaryDict.expectedOnHover);
|
|
||||||
|
|
||||||
cy.get(`#${entry.id}`).click();
|
|
||||||
|
|
||||||
rightTextCheck(methodDict.pathRight, methodDict.expectedText);
|
|
||||||
rightTextCheck(protocolDict.pathRight, protocolDict.expectedTextRight);
|
|
||||||
if (shouldCheckSummary)
|
|
||||||
rightTextCheck(summaryDict.pathRight, summaryDict.expectedText);
|
|
||||||
|
|
||||||
rightOnHoverCheck(methodDict.pathRight, methodDict.expectedOnHover);
|
|
||||||
rightOnHoverCheck(protocolDict.pathRight, protocolDict.expectedOnHover);
|
|
||||||
if (shouldCheckSummary)
|
|
||||||
rightOnHoverCheck(summaryDict.pathRight, summaryDict.expectedOnHover);
|
|
||||||
|
|
||||||
if (valueTab) {
|
|
||||||
if (valueTab === valueTabs.response)
|
|
||||||
cy.contains('Response').click();
|
|
||||||
cy.get(Cypress.env('bodyJsonClass')).then(text => {
|
|
||||||
expect(text.text()).to.match(valueRegex)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSummeryDict() {
|
|
||||||
return {
|
|
||||||
pathLeft: '> :nth-child(2) > :nth-child(1) > :nth-child(2) > :nth-child(2)',
|
|
||||||
pathRight: '> :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(2) > :nth-child(2)',
|
|
||||||
expectedText: 'key',
|
|
||||||
expectedOnHover: `summary == "key"`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMethodDict(method) {
|
|
||||||
return {
|
|
||||||
pathLeft: '> :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(2)',
|
|
||||||
pathRight: '> :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(1) > :nth-child(2)',
|
|
||||||
expectedText: method,
|
|
||||||
expectedOnHover: `method == "${method}"`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getProtocolDict() {
|
|
||||||
return {
|
|
||||||
pathLeft: '> :nth-child(1) > :nth-child(1)',
|
|
||||||
pathRight: '> :nth-child(1) > :nth-child(1) > :nth-child(1) > :nth-child(1)',
|
|
||||||
expectedTextLeft: 'REDIS',
|
|
||||||
expectedTextRight: 'Redis Serialization Protocol',
|
|
||||||
expectedOnHover: `redis`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getEntryNumById (id) {
|
|
||||||
return parseInt(id.split('-')[1]);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import {isValueExistsInElement, verifyMinimumEntries} from "../testHelpers/TrafficHelper";
|
import {isValueExistsInElement} from "../testHelpers/TrafficHelper";
|
||||||
|
|
||||||
it('Loading Mizu', function () {
|
it('Loading Mizu', function () {
|
||||||
cy.visit(Cypress.env('testUrl'));
|
cy.visit(Cypress.env('testUrl'));
|
||||||
});
|
});
|
||||||
|
|
||||||
verifyMinimumEntries();
|
|
||||||
|
|
||||||
isValueExistsInElement(true, Cypress.env('regexMaskingBodyContent'), Cypress.env('bodyJsonClass'));
|
isValueExistsInElement(true, Cypress.env('regexMaskingBodyContent'), Cypress.env('bodyJsonClass'));
|
||||||
|
|||||||
@@ -1,15 +1,24 @@
|
|||||||
import {findLineAndCheck, getExpectedDetailsDict} from "../testHelpers/StatusBarHelper";
|
import {findLineAndCheck, getExpectedDetailsDict} from "../testHelpers/StatusBarHelper";
|
||||||
import {
|
import {
|
||||||
|
leftOnHoverCheck,
|
||||||
leftTextCheck,
|
leftTextCheck,
|
||||||
resizeToHugeMizu,
|
resizeToHugeMizu,
|
||||||
resizeToNormalMizu,
|
resizeToNormalMizu,
|
||||||
rightOnHoverCheck,
|
rightOnHoverCheck,
|
||||||
leftOnHoverCheck,
|
|
||||||
rightTextCheck,
|
rightTextCheck,
|
||||||
verifyMinimumEntries
|
verifyMinimumEntries
|
||||||
} from "../testHelpers/TrafficHelper";
|
} from "../testHelpers/TrafficHelper";
|
||||||
|
|
||||||
const refreshWaitTimeout = 10000;
|
const refreshWaitTimeout = 10000;
|
||||||
|
|
||||||
|
|
||||||
|
const fullParam = Cypress.env('arrayDict'); // "Name:fooNamespace:barName:foo1Namespace:bar1"
|
||||||
|
const podsArray = fullParam.split('Name:').slice(1); // ["fooNamespace:bar", "foo1Namespace:bar1"]
|
||||||
|
podsArray.forEach((podStr, index) => {
|
||||||
|
const podAndNamespaceArr = podStr.split('Namespace:'); // [foo, bar] / [foo1, bar1]
|
||||||
|
podsArray[index] = getExpectedDetailsDict(podAndNamespaceArr[0], podAndNamespaceArr[1]);
|
||||||
|
});
|
||||||
|
|
||||||
it('opening mizu', function () {
|
it('opening mizu', function () {
|
||||||
cy.visit(Cypress.env('testUrl'));
|
cy.visit(Cypress.env('testUrl'));
|
||||||
});
|
});
|
||||||
@@ -17,16 +26,13 @@ it('opening mizu', function () {
|
|||||||
verifyMinimumEntries();
|
verifyMinimumEntries();
|
||||||
|
|
||||||
it('top bar check', function () {
|
it('top bar check', function () {
|
||||||
const podName1 = 'httpbin', namespace1 = 'mizu-tests';
|
|
||||||
const podName2 = 'httpbin2', namespace2 = 'mizu-tests';
|
|
||||||
|
|
||||||
cy.get('.podsCount').trigger('mouseover');
|
cy.get('.podsCount').trigger('mouseover');
|
||||||
findLineAndCheck(getExpectedDetailsDict(podName1, namespace1));
|
podsArray.map(findLineAndCheck);
|
||||||
findLineAndCheck(getExpectedDetailsDict(podName2, namespace2));
|
|
||||||
cy.reload();
|
cy.reload();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('filtering guide check', function () {
|
it('filtering guide check', function () {
|
||||||
|
cy.reload();
|
||||||
cy.get('[title="Open Filtering Guide (Cheatsheet)"]').click();
|
cy.get('[title="Open Filtering Guide (Cheatsheet)"]').click();
|
||||||
cy.get('#modal-modal-title').should('be.visible');
|
cy.get('#modal-modal-title').should('be.visible');
|
||||||
cy.get('[lang="en"]').click(0, 0);
|
cy.get('[lang="en"]').click(0, 0);
|
||||||
@@ -84,14 +90,27 @@ checkFilter({
|
|||||||
applyByEnter: false
|
applyByEnter: false
|
||||||
});
|
});
|
||||||
|
|
||||||
checkFilter({
|
if (Cypress.env('shouldCheckSrcAndDest')) {
|
||||||
name: 'src.name == ""',
|
serviceMapCheck();
|
||||||
leftSidePath: '[title="Source Name"]',
|
|
||||||
leftSideExpectedText: '[Unresolved]',
|
checkFilter({
|
||||||
rightSidePath: '> :nth-child(2) [title="Source Name"]',
|
name: 'src.name == ""',
|
||||||
rightSideExpectedText: '[Unresolved]',
|
leftSidePath: '[title="Source Name"]',
|
||||||
applyByEnter: false
|
leftSideExpectedText: '[Unresolved]',
|
||||||
});
|
rightSidePath: '> :nth-child(2) [title="Source Name"]',
|
||||||
|
rightSideExpectedText: '[Unresolved]',
|
||||||
|
applyByEnter: false
|
||||||
|
});
|
||||||
|
|
||||||
|
checkFilter({
|
||||||
|
name: `dst.name == "httpbin.mizu-tests"`,
|
||||||
|
leftSidePath: '> :nth-child(3) > :nth-child(2) > :nth-child(3) > :nth-child(2)',
|
||||||
|
leftSideExpectedText: 'httpbin.mizu-tests',
|
||||||
|
rightSidePath: '> :nth-child(2) > :nth-child(2) > :nth-child(2) > :nth-child(3) > :nth-child(2)',
|
||||||
|
rightSideExpectedText: 'httpbin.mizu-tests',
|
||||||
|
applyByEnter: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
checkFilter({
|
checkFilter({
|
||||||
name: 'method == "GET"',
|
name: 'method == "GET"',
|
||||||
@@ -111,15 +130,6 @@ checkFilter({
|
|||||||
applyByEnter: false
|
applyByEnter: false
|
||||||
});
|
});
|
||||||
|
|
||||||
checkFilter({
|
|
||||||
name: 'dst.name == "httpbin.mizu-tests"',
|
|
||||||
leftSidePath: '> :nth-child(3) > :nth-child(2) > :nth-child(3) > :nth-child(2)',
|
|
||||||
leftSideExpectedText: 'httpbin.mizu-tests',
|
|
||||||
rightSidePath: '> :nth-child(2) > :nth-child(2) > :nth-child(2) > :nth-child(3) > :nth-child(2)',
|
|
||||||
rightSideExpectedText: 'httpbin.mizu-tests',
|
|
||||||
applyByEnter: false
|
|
||||||
});
|
|
||||||
|
|
||||||
checkFilter({
|
checkFilter({
|
||||||
name: 'src.ip == "127.0.0.1"',
|
name: 'src.ip == "127.0.0.1"',
|
||||||
leftSidePath: '[title="Source IP"]',
|
leftSidePath: '[title="Source IP"]',
|
||||||
@@ -167,6 +177,7 @@ function shouldNotExist(entryNum) {
|
|||||||
|
|
||||||
function checkIllegalFilter(illegalFilterName) {
|
function checkIllegalFilter(illegalFilterName) {
|
||||||
it(`should show red search bar with the input: ${illegalFilterName}`, function () {
|
it(`should show red search bar with the input: ${illegalFilterName}`, function () {
|
||||||
|
cy.reload();
|
||||||
cy.get('#total-entries').then(number => {
|
cy.get('#total-entries').then(number => {
|
||||||
const totalEntries = number.text();
|
const totalEntries = number.text();
|
||||||
|
|
||||||
@@ -188,7 +199,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 +331,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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/go-redis/redis/v8"
|
"github.com/go-redis/redis/v8"
|
||||||
|
amqp "github.com/rabbitmq/amqp091-go"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRedis(t *testing.T) {
|
func TestRedis(t *testing.T) {
|
||||||
@@ -13,22 +15,22 @@ func TestRedis(t *testing.T) {
|
|||||||
t.Skip("ignored acceptance test")
|
t.Skip("ignored acceptance test")
|
||||||
}
|
}
|
||||||
|
|
||||||
cliPath, cliPathErr := getCliPath()
|
cliPath, cliPathErr := GetCliPath()
|
||||||
if cliPathErr != nil {
|
if cliPathErr != nil {
|
||||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tapCmdArgs := getDefaultTapCommandArgs()
|
tapCmdArgs := GetDefaultTapCommandArgs()
|
||||||
|
|
||||||
tapNamespace := getDefaultTapNamespace()
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
||||||
t.Logf("running command: %v", tapCmd.String())
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
if err := cleanupCommand(tapCmd); err != nil {
|
if err := CleanupCommand(tapCmd); err != nil {
|
||||||
t.Logf("failed to cleanup tap command, err: %v", err)
|
t.Logf("failed to cleanup tap command, err: %v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -38,16 +40,22 @@ func TestRedis(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiServerUrl := getApiServerUrl(defaultApiServerPort)
|
apiServerUrl := GetApiServerUrl(defaultApiServerPort)
|
||||||
|
|
||||||
if err := waitTapPodsReady(apiServerUrl); err != nil {
|
if err := WaitTapPodsReady(apiServerUrl); err != nil {
|
||||||
t.Errorf("failed to start tap pods on time, err: %v", err)
|
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
redisExternalIp, err := getServiceExternalIp(ctx, defaultNamespaceName, "redis")
|
kubernetesProvider, err := NewKubernetesProvider()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to create k8s provider, err %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
redisExternalIp, err := kubernetesProvider.GetServiceExternalIp(ctx, defaultNamespaceName, "redis")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to get redis external ip, err: %v", err)
|
t.Errorf("failed to get redis external ip, err: %v", err)
|
||||||
return
|
return
|
||||||
@@ -97,5 +105,136 @@ func TestRedis(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/Redis.js\"")
|
RunCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/Redis.js\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAmqp(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("ignored acceptance test")
|
||||||
|
}
|
||||||
|
|
||||||
|
cliPath, cliPathErr := GetCliPath()
|
||||||
|
if cliPathErr != nil {
|
||||||
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tapCmdArgs := GetDefaultTapCommandArgs()
|
||||||
|
|
||||||
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
|
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
||||||
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err := CleanupCommand(tapCmd); err != nil {
|
||||||
|
t.Logf("failed to cleanup tap command, err: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := tapCmd.Start(); err != nil {
|
||||||
|
t.Errorf("failed to start tap command, err: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
apiServerUrl := GetApiServerUrl(defaultApiServerPort)
|
||||||
|
|
||||||
|
if err := WaitTapPodsReady(apiServerUrl); err != nil {
|
||||||
|
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
kubernetesProvider, err := NewKubernetesProvider()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to create k8s provider, err %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rabbitmqExternalIp, err := kubernetesProvider.GetServiceExternalIp(ctx, defaultNamespaceName, "rabbitmq")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to get RabbitMQ external ip, err: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := amqp.Dial(fmt.Sprintf("amqp://guest:guest@%v:5672/", rabbitmqExternalIp))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to connect to RabbitMQ, err: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
// Temporary fix for missing amqp entries
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
|
||||||
|
for i := 0; i < defaultEntriesCount/5; i++ {
|
||||||
|
ch, err := conn.Channel()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to open a channel, err: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
exchangeName := "exchange"
|
||||||
|
err = ch.ExchangeDeclare(exchangeName, "direct", true, false, false, false, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to declare an exchange, err: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
q, err := ch.QueueDeclare("queue", true, false, false, false, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to declare a queue, err: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
routingKey := "routing_key"
|
||||||
|
err = ch.QueueBind(q.Name, routingKey, exchangeName, false, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to bind the queue, err: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ch.Publish(exchangeName, routingKey, false, false,
|
||||||
|
amqp.Publishing{
|
||||||
|
DeliveryMode: amqp.Persistent,
|
||||||
|
ContentType: "text/plain",
|
||||||
|
Body: []byte("message"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to publish a message, err: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msgChan, err := ch.Consume(q.Name, "Consumer", true, false, false, false, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to create a consumer, err: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-msgChan:
|
||||||
|
break
|
||||||
|
case <-time.After(3 * time.Second):
|
||||||
|
t.Errorf("failed to consume a message on time")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ch.ExchangeDelete(exchangeName, false, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to delete the exchange, err: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ch.QueueDelete(q.Name, false, false, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to delete the queue, err: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ch.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
RunCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/Rabbit.js\"")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
module github.com/up9inc/mizu/tests
|
module github.com/up9inc/mizu/acceptanceTests
|
||||||
|
|
||||||
go 1.17
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-redis/redis/v8 v8.11.4
|
github.com/go-redis/redis/v8 v8.11.4
|
||||||
|
github.com/rabbitmq/amqp091-go v1.3.0
|
||||||
github.com/up9inc/mizu/shared v0.0.0
|
github.com/up9inc/mizu/shared v0.0.0
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||||
k8s.io/apimachinery v0.23.3
|
k8s.io/apimachinery v0.23.3
|
||||||
|
|||||||
@@ -427,6 +427,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
|
|||||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
|
github.com/rabbitmq/amqp091-go v1.3.0 h1:A/QuHiNw7LMCJsxx9iZn5lrIz6OrhIn7Dfk5/1YatWM=
|
||||||
|
github.com/rabbitmq/amqp091-go v1.3.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM=
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
|
|||||||
@@ -11,22 +11,22 @@ func TestLogs(t *testing.T) {
|
|||||||
t.Skip("ignored acceptance test")
|
t.Skip("ignored acceptance test")
|
||||||
}
|
}
|
||||||
|
|
||||||
cliPath, cliPathErr := getCliPath()
|
cliPath, cliPathErr := GetCliPath()
|
||||||
if cliPathErr != nil {
|
if cliPathErr != nil {
|
||||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tapCmdArgs := getDefaultTapCommandArgs()
|
tapCmdArgs := GetDefaultTapCommandArgs()
|
||||||
|
|
||||||
tapNamespace := getDefaultTapNamespace()
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
||||||
t.Logf("running command: %v", tapCmd.String())
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
if err := cleanupCommand(tapCmd); err != nil {
|
if err := CleanupCommand(tapCmd); err != nil {
|
||||||
t.Logf("failed to cleanup tap command, err: %v", err)
|
t.Logf("failed to cleanup tap command, err: %v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -36,14 +36,14 @@ func TestLogs(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiServerUrl := getApiServerUrl(defaultApiServerPort)
|
apiServerUrl := GetApiServerUrl(defaultApiServerPort)
|
||||||
|
|
||||||
if err := waitTapPodsReady(apiServerUrl); err != nil {
|
if err := WaitTapPodsReady(apiServerUrl); err != nil {
|
||||||
t.Errorf("failed to start tap pods on time, err: %v", err)
|
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logsCmdArgs := getDefaultLogsCommandArgs()
|
logsCmdArgs := GetDefaultLogsCommandArgs()
|
||||||
|
|
||||||
logsCmd := exec.Command(cliPath, logsCmdArgs...)
|
logsCmd := exec.Command(cliPath, logsCmdArgs...)
|
||||||
t.Logf("running command: %v", logsCmd.String())
|
t.Logf("running command: %v", logsCmd.String())
|
||||||
@@ -58,7 +58,7 @@ func TestLogs(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logsPath, logsPathErr := getLogsPath()
|
logsPath, logsPathErr := GetLogsPath()
|
||||||
if logsPathErr != nil {
|
if logsPathErr != nil {
|
||||||
t.Errorf("failed to get logs path, err: %v", logsPathErr)
|
t.Errorf("failed to get logs path, err: %v", logsPathErr)
|
||||||
return
|
return
|
||||||
@@ -112,22 +112,22 @@ func TestLogsPath(t *testing.T) {
|
|||||||
t.Skip("ignored acceptance test")
|
t.Skip("ignored acceptance test")
|
||||||
}
|
}
|
||||||
|
|
||||||
cliPath, cliPathErr := getCliPath()
|
cliPath, cliPathErr := GetCliPath()
|
||||||
if cliPathErr != nil {
|
if cliPathErr != nil {
|
||||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tapCmdArgs := getDefaultTapCommandArgs()
|
tapCmdArgs := GetDefaultTapCommandArgs()
|
||||||
|
|
||||||
tapNamespace := getDefaultTapNamespace()
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
||||||
t.Logf("running command: %v", tapCmd.String())
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
if err := cleanupCommand(tapCmd); err != nil {
|
if err := CleanupCommand(tapCmd); err != nil {
|
||||||
t.Logf("failed to cleanup tap command, err: %v", err)
|
t.Logf("failed to cleanup tap command, err: %v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -137,14 +137,14 @@ func TestLogsPath(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiServerUrl := getApiServerUrl(defaultApiServerPort)
|
apiServerUrl := GetApiServerUrl(defaultApiServerPort)
|
||||||
|
|
||||||
if err := waitTapPodsReady(apiServerUrl); err != nil {
|
if err := WaitTapPodsReady(apiServerUrl); err != nil {
|
||||||
t.Errorf("failed to start tap pods on time, err: %v", err)
|
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logsCmdArgs := getDefaultLogsCommandArgs()
|
logsCmdArgs := GetDefaultLogsCommandArgs()
|
||||||
|
|
||||||
logsPath := "../logs.zip"
|
logsPath := "../logs.zip"
|
||||||
logsCmdArgs = append(logsCmdArgs, "-f", logsPath)
|
logsCmdArgs = append(logsCmdArgs, "-f", logsPath)
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ kubectl create deployment httpbin --image=kennethreitz/httpbin -n mizu-tests2
|
|||||||
echo "Creating redis deployment"
|
echo "Creating redis deployment"
|
||||||
kubectl create deployment redis --image=redis -n mizu-tests
|
kubectl create deployment redis --image=redis -n mizu-tests
|
||||||
|
|
||||||
|
echo "Creating rabbitmq deployment"
|
||||||
|
kubectl create deployment rabbitmq --image=rabbitmq -n mizu-tests
|
||||||
|
|
||||||
echo "Creating httpbin services"
|
echo "Creating httpbin services"
|
||||||
kubectl expose deployment httpbin --type=NodePort --port=80 -n mizu-tests
|
kubectl expose deployment httpbin --type=NodePort --port=80 -n mizu-tests
|
||||||
kubectl expose deployment httpbin2 --type=NodePort --port=80 -n mizu-tests
|
kubectl expose deployment httpbin2 --type=NodePort --port=80 -n mizu-tests
|
||||||
@@ -48,12 +51,12 @@ kubectl expose deployment httpbin --type=NodePort --port=80 -n mizu-tests2
|
|||||||
echo "Creating redis service"
|
echo "Creating redis service"
|
||||||
kubectl expose deployment redis --type=LoadBalancer --port=6379 -n mizu-tests
|
kubectl expose deployment redis --type=LoadBalancer --port=6379 -n mizu-tests
|
||||||
|
|
||||||
|
echo "Creating rabbitmq service"
|
||||||
|
kubectl expose deployment rabbitmq --type=LoadBalancer --port=5672 -n mizu-tests
|
||||||
|
|
||||||
echo "Starting proxy"
|
echo "Starting proxy"
|
||||||
kubectl proxy --port=8080 &
|
kubectl proxy --port=8080 &
|
||||||
|
|
||||||
echo "Starting tunnel"
|
|
||||||
minikube tunnel &
|
|
||||||
|
|
||||||
echo "Setting minikube docker env"
|
echo "Setting minikube docker env"
|
||||||
eval $(minikube docker-env)
|
eval $(minikube docker-env)
|
||||||
|
|
||||||
@@ -62,3 +65,6 @@ make build-docker-ci
|
|||||||
|
|
||||||
echo "Build cli"
|
echo "Build cli"
|
||||||
make build-cli-ci
|
make build-cli-ci
|
||||||
|
|
||||||
|
echo "Starting tunnel"
|
||||||
|
minikube tunnel &
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestTap(t *testing.T) {
|
func TestTap(t *testing.T) {
|
||||||
|
basicTapTest(t, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func basicTapTest(t *testing.T, shouldCheckSrcAndDest bool, extraArgs... string) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("ignored acceptance test")
|
t.Skip("ignored acceptance test")
|
||||||
}
|
}
|
||||||
@@ -22,22 +26,24 @@ func TestTap(t *testing.T) {
|
|||||||
|
|
||||||
for _, entriesCount := range tests {
|
for _, entriesCount := range tests {
|
||||||
t.Run(fmt.Sprintf("%d", entriesCount), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%d", entriesCount), func(t *testing.T) {
|
||||||
cliPath, cliPathErr := getCliPath()
|
cliPath, cliPathErr := GetCliPath()
|
||||||
if cliPathErr != nil {
|
if cliPathErr != nil {
|
||||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tapCmdArgs := getDefaultTapCommandArgs()
|
tapCmdArgs := GetDefaultTapCommandArgs()
|
||||||
|
|
||||||
tapNamespace := getDefaultTapNamespace()
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
|
tapCmdArgs = append(tapCmdArgs, extraArgs...)
|
||||||
|
|
||||||
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
||||||
t.Logf("running command: %v", tapCmd.String())
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
if err := cleanupCommand(tapCmd); err != nil {
|
if err := CleanupCommand(tapCmd); err != nil {
|
||||||
t.Logf("failed to cleanup tap command, err: %v", err)
|
t.Logf("failed to cleanup tap command, err: %v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -47,22 +53,33 @@ func TestTap(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiServerUrl := getApiServerUrl(defaultApiServerPort)
|
apiServerUrl := GetApiServerUrl(defaultApiServerPort)
|
||||||
|
|
||||||
if err := waitTapPodsReady(apiServerUrl); err != nil {
|
if err := WaitTapPodsReady(apiServerUrl); err != nil {
|
||||||
t.Errorf("failed to start tap pods on time, err: %v", err)
|
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyUrl := getProxyUrl(defaultNamespaceName, defaultServiceName)
|
proxyUrl := GetProxyUrl(defaultNamespaceName, defaultServiceName)
|
||||||
for i := 0; i < entriesCount; i++ {
|
for i := 0; i < entriesCount; i++ {
|
||||||
if _, requestErr := executeHttpGetRequest(fmt.Sprintf("%v/get", proxyUrl)); requestErr != nil {
|
if _, requestErr := ExecuteHttpGetRequest(fmt.Sprintf("%v/get", proxyUrl)); requestErr != nil {
|
||||||
t.Errorf("failed to send proxy request, err: %v", requestErr)
|
t.Errorf("failed to send proxy request, err: %v", requestErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/UiTest.js\"")
|
expectedPods := []PodDescriptor{
|
||||||
|
{Name: "httpbin", Namespace: "mizu-tests"},
|
||||||
|
{Name: "httpbin2", Namespace: "mizu-tests"},
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectedPodsStr string
|
||||||
|
for i := 0; i < len(expectedPods); i++ {
|
||||||
|
expectedPodsStr += fmt.Sprintf("Name:%vNamespace:%v", expectedPods[i].Name, expectedPods[i].Namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
RunCypressTests(t, fmt.Sprintf("npx cypress run --spec \"cypress/integration/tests/UiTest.js\" --env entriesCount=%d,arrayDict=%v,shouldCheckSrcAndDest=%v",
|
||||||
|
entriesCount, expectedPodsStr, shouldCheckSrcAndDest))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,15 +93,15 @@ func TestTapGuiPort(t *testing.T) {
|
|||||||
|
|
||||||
for _, guiPort := range tests {
|
for _, guiPort := range tests {
|
||||||
t.Run(fmt.Sprintf("%d", guiPort), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%d", guiPort), func(t *testing.T) {
|
||||||
cliPath, cliPathErr := getCliPath()
|
cliPath, cliPathErr := GetCliPath()
|
||||||
if cliPathErr != nil {
|
if cliPathErr != nil {
|
||||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tapCmdArgs := getDefaultTapCommandArgs()
|
tapCmdArgs := GetDefaultTapCommandArgs()
|
||||||
|
|
||||||
tapNamespace := getDefaultTapNamespace()
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
tapCmdArgs = append(tapCmdArgs, "-p", fmt.Sprintf("%d", guiPort))
|
tapCmdArgs = append(tapCmdArgs, "-p", fmt.Sprintf("%d", guiPort))
|
||||||
@@ -93,7 +110,7 @@ func TestTapGuiPort(t *testing.T) {
|
|||||||
t.Logf("running command: %v", tapCmd.String())
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
if err := cleanupCommand(tapCmd); err != nil {
|
if err := CleanupCommand(tapCmd); err != nil {
|
||||||
t.Logf("failed to cleanup tap command, err: %v", err)
|
t.Logf("failed to cleanup tap command, err: %v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -103,22 +120,22 @@ func TestTapGuiPort(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiServerUrl := getApiServerUrl(guiPort)
|
apiServerUrl := GetApiServerUrl(guiPort)
|
||||||
|
|
||||||
if err := waitTapPodsReady(apiServerUrl); err != nil {
|
if err := WaitTapPodsReady(apiServerUrl); err != nil {
|
||||||
t.Errorf("failed to start tap pods on time, err: %v", err)
|
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyUrl := getProxyUrl(defaultNamespaceName, defaultServiceName)
|
proxyUrl := GetProxyUrl(defaultNamespaceName, defaultServiceName)
|
||||||
for i := 0; i < defaultEntriesCount; i++ {
|
for i := 0; i < defaultEntriesCount; i++ {
|
||||||
if _, requestErr := executeHttpGetRequest(fmt.Sprintf("%v/get", proxyUrl)); requestErr != nil {
|
if _, requestErr := ExecuteHttpGetRequest(fmt.Sprintf("%v/get", proxyUrl)); requestErr != nil {
|
||||||
t.Errorf("failed to send proxy request, err: %v", requestErr)
|
t.Errorf("failed to send proxy request, err: %v", requestErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runCypressTests(t, fmt.Sprintf("npx cypress run --spec \"cypress/integration/tests/GuiPort.js\" --env name=%v,namespace=%v,port=%d",
|
RunCypressTests(t, fmt.Sprintf("npx cypress run --spec \"cypress/integration/tests/GuiPort.js\" --env name=%v,namespace=%v,port=%d",
|
||||||
"httpbin", "mizu-tests", guiPort))
|
"httpbin", "mizu-tests", guiPort))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -135,20 +152,20 @@ func TestTapAllNamespaces(t *testing.T) {
|
|||||||
{Name: "httpbin", Namespace: "mizu-tests2"},
|
{Name: "httpbin", Namespace: "mizu-tests2"},
|
||||||
}
|
}
|
||||||
|
|
||||||
cliPath, cliPathErr := getCliPath()
|
cliPath, cliPathErr := GetCliPath()
|
||||||
if cliPathErr != nil {
|
if cliPathErr != nil {
|
||||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tapCmdArgs := getDefaultTapCommandArgs()
|
tapCmdArgs := GetDefaultTapCommandArgs()
|
||||||
tapCmdArgs = append(tapCmdArgs, "-A")
|
tapCmdArgs = append(tapCmdArgs, "-A")
|
||||||
|
|
||||||
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
||||||
t.Logf("running command: %v", tapCmd.String())
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
if err := cleanupCommand(tapCmd); err != nil {
|
if err := CleanupCommand(tapCmd); err != nil {
|
||||||
t.Logf("failed to cleanup tap command, err: %v", err)
|
t.Logf("failed to cleanup tap command, err: %v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -158,14 +175,14 @@ func TestTapAllNamespaces(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiServerUrl := getApiServerUrl(defaultApiServerPort)
|
apiServerUrl := GetApiServerUrl(defaultApiServerPort)
|
||||||
|
|
||||||
if err := waitTapPodsReady(apiServerUrl); err != nil {
|
if err := WaitTapPodsReady(apiServerUrl); err != nil {
|
||||||
t.Errorf("failed to start tap pods on time, err: %v", err)
|
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
runCypressTests(t, fmt.Sprintf("npx cypress run --spec \"cypress/integration/tests/MultipleNamespaces.js\" --env name1=%v,name2=%v,name3=%v,namespace1=%v,namespace2=%v,namespace3=%v",
|
RunCypressTests(t, fmt.Sprintf("npx cypress run --spec \"cypress/integration/tests/MultipleNamespaces.js\" --env name1=%v,name2=%v,name3=%v,namespace1=%v,namespace2=%v,namespace3=%v",
|
||||||
expectedPods[0].Name, expectedPods[1].Name, expectedPods[2].Name, expectedPods[0].Namespace, expectedPods[1].Namespace, expectedPods[2].Namespace))
|
expectedPods[0].Name, expectedPods[1].Name, expectedPods[2].Name, expectedPods[0].Namespace, expectedPods[1].Namespace, expectedPods[2].Namespace))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,13 +197,13 @@ func TestTapMultipleNamespaces(t *testing.T) {
|
|||||||
{Name: "httpbin", Namespace: "mizu-tests2"},
|
{Name: "httpbin", Namespace: "mizu-tests2"},
|
||||||
}
|
}
|
||||||
|
|
||||||
cliPath, cliPathErr := getCliPath()
|
cliPath, cliPathErr := GetCliPath()
|
||||||
if cliPathErr != nil {
|
if cliPathErr != nil {
|
||||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tapCmdArgs := getDefaultTapCommandArgs()
|
tapCmdArgs := GetDefaultTapCommandArgs()
|
||||||
var namespacesCmd []string
|
var namespacesCmd []string
|
||||||
for _, expectedPod := range expectedPods {
|
for _, expectedPod := range expectedPods {
|
||||||
namespacesCmd = append(namespacesCmd, "-n", expectedPod.Namespace)
|
namespacesCmd = append(namespacesCmd, "-n", expectedPod.Namespace)
|
||||||
@@ -197,7 +214,7 @@ func TestTapMultipleNamespaces(t *testing.T) {
|
|||||||
t.Logf("running command: %v", tapCmd.String())
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
if err := cleanupCommand(tapCmd); err != nil {
|
if err := CleanupCommand(tapCmd); err != nil {
|
||||||
t.Logf("failed to cleanup tap command, err: %v", err)
|
t.Logf("failed to cleanup tap command, err: %v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -207,14 +224,14 @@ func TestTapMultipleNamespaces(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiServerUrl := getApiServerUrl(defaultApiServerPort)
|
apiServerUrl := GetApiServerUrl(defaultApiServerPort)
|
||||||
|
|
||||||
if err := waitTapPodsReady(apiServerUrl); err != nil {
|
if err := WaitTapPodsReady(apiServerUrl); err != nil {
|
||||||
t.Errorf("failed to start tap pods on time, err: %v", err)
|
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
runCypressTests(t, fmt.Sprintf("npx cypress run --spec \"cypress/integration/tests/MultipleNamespaces.js\" --env name1=%v,name2=%v,name3=%v,namespace1=%v,namespace2=%v,namespace3=%v",
|
RunCypressTests(t, fmt.Sprintf("npx cypress run --spec \"cypress/integration/tests/MultipleNamespaces.js\" --env name1=%v,name2=%v,name3=%v,namespace1=%v,namespace2=%v,namespace3=%v",
|
||||||
expectedPods[0].Name, expectedPods[1].Name, expectedPods[2].Name, expectedPods[0].Namespace, expectedPods[1].Namespace, expectedPods[2].Namespace))
|
expectedPods[0].Name, expectedPods[1].Name, expectedPods[2].Name, expectedPods[0].Namespace, expectedPods[1].Namespace, expectedPods[2].Namespace))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,22 +245,22 @@ func TestTapRegex(t *testing.T) {
|
|||||||
{Name: regexPodName, Namespace: "mizu-tests"},
|
{Name: regexPodName, Namespace: "mizu-tests"},
|
||||||
}
|
}
|
||||||
|
|
||||||
cliPath, cliPathErr := getCliPath()
|
cliPath, cliPathErr := GetCliPath()
|
||||||
if cliPathErr != nil {
|
if cliPathErr != nil {
|
||||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tapCmdArgs := getDefaultTapCommandArgsWithRegex(regexPodName)
|
tapCmdArgs := GetDefaultTapCommandArgsWithRegex(regexPodName)
|
||||||
|
|
||||||
tapNamespace := getDefaultTapNamespace()
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
||||||
t.Logf("running command: %v", tapCmd.String())
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
if err := cleanupCommand(tapCmd); err != nil {
|
if err := CleanupCommand(tapCmd); err != nil {
|
||||||
t.Logf("failed to cleanup tap command, err: %v", err)
|
t.Logf("failed to cleanup tap command, err: %v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -253,14 +270,14 @@ func TestTapRegex(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiServerUrl := getApiServerUrl(defaultApiServerPort)
|
apiServerUrl := GetApiServerUrl(defaultApiServerPort)
|
||||||
|
|
||||||
if err := waitTapPodsReady(apiServerUrl); err != nil {
|
if err := WaitTapPodsReady(apiServerUrl); err != nil {
|
||||||
t.Errorf("failed to start tap pods on time, err: %v", err)
|
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
runCypressTests(t, fmt.Sprintf("npx cypress run --spec \"cypress/integration/tests/Regex.js\" --env name=%v,namespace=%v",
|
RunCypressTests(t, fmt.Sprintf("npx cypress run --spec \"cypress/integration/tests/Regex.js\" --env name=%v,namespace=%v",
|
||||||
expectedPods[0].Name, expectedPods[0].Namespace))
|
expectedPods[0].Name, expectedPods[0].Namespace))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,15 +286,15 @@ func TestTapDryRun(t *testing.T) {
|
|||||||
t.Skip("ignored acceptance test")
|
t.Skip("ignored acceptance test")
|
||||||
}
|
}
|
||||||
|
|
||||||
cliPath, cliPathErr := getCliPath()
|
cliPath, cliPathErr := GetCliPath()
|
||||||
if cliPathErr != nil {
|
if cliPathErr != nil {
|
||||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tapCmdArgs := getDefaultTapCommandArgs()
|
tapCmdArgs := GetDefaultTapCommandArgs()
|
||||||
|
|
||||||
tapNamespace := getDefaultTapNamespace()
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
tapCmdArgs = append(tapCmdArgs, "--dry-run")
|
tapCmdArgs = append(tapCmdArgs, "--dry-run")
|
||||||
@@ -316,22 +333,22 @@ func TestTapRedact(t *testing.T) {
|
|||||||
t.Skip("ignored acceptance test")
|
t.Skip("ignored acceptance test")
|
||||||
}
|
}
|
||||||
|
|
||||||
cliPath, cliPathErr := getCliPath()
|
cliPath, cliPathErr := GetCliPath()
|
||||||
if cliPathErr != nil {
|
if cliPathErr != nil {
|
||||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tapCmdArgs := getDefaultTapCommandArgs()
|
tapCmdArgs := GetDefaultTapCommandArgs()
|
||||||
|
|
||||||
tapNamespace := getDefaultTapNamespace()
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
||||||
t.Logf("running command: %v", tapCmd.String())
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
if err := cleanupCommand(tapCmd); err != nil {
|
if err := CleanupCommand(tapCmd); err != nil {
|
||||||
t.Logf("failed to cleanup tap command, err: %v", err)
|
t.Logf("failed to cleanup tap command, err: %v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -341,24 +358,24 @@ func TestTapRedact(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiServerUrl := getApiServerUrl(defaultApiServerPort)
|
apiServerUrl := GetApiServerUrl(defaultApiServerPort)
|
||||||
|
|
||||||
if err := waitTapPodsReady(apiServerUrl); err != nil {
|
if err := WaitTapPodsReady(apiServerUrl); err != nil {
|
||||||
t.Errorf("failed to start tap pods on time, err: %v", err)
|
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyUrl := getProxyUrl(defaultNamespaceName, defaultServiceName)
|
proxyUrl := GetProxyUrl(defaultNamespaceName, defaultServiceName)
|
||||||
requestHeaders := map[string]string{"User-Header": "Mizu"}
|
requestHeaders := map[string]string{"User-Header": "Mizu"}
|
||||||
requestBody := map[string]string{"User": "Mizu"}
|
requestBody := map[string]string{"User": "Mizu"}
|
||||||
for i := 0; i < defaultEntriesCount; i++ {
|
for i := 0; i < defaultEntriesCount; i++ {
|
||||||
if _, requestErr := executeHttpPostRequestWithHeaders(fmt.Sprintf("%v/post", proxyUrl), requestHeaders, requestBody); requestErr != nil {
|
if _, requestErr := ExecuteHttpPostRequestWithHeaders(fmt.Sprintf("%v/post", proxyUrl), requestHeaders, requestBody); requestErr != nil {
|
||||||
t.Errorf("failed to send proxy request, err: %v", requestErr)
|
t.Errorf("failed to send proxy request, err: %v", requestErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/Redact.js\"")
|
RunCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/Redact.js\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTapNoRedact(t *testing.T) {
|
func TestTapNoRedact(t *testing.T) {
|
||||||
@@ -366,15 +383,15 @@ func TestTapNoRedact(t *testing.T) {
|
|||||||
t.Skip("ignored acceptance test")
|
t.Skip("ignored acceptance test")
|
||||||
}
|
}
|
||||||
|
|
||||||
cliPath, cliPathErr := getCliPath()
|
cliPath, cliPathErr := GetCliPath()
|
||||||
if cliPathErr != nil {
|
if cliPathErr != nil {
|
||||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tapCmdArgs := getDefaultTapCommandArgs()
|
tapCmdArgs := GetDefaultTapCommandArgs()
|
||||||
|
|
||||||
tapNamespace := getDefaultTapNamespace()
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
tapCmdArgs = append(tapCmdArgs, "--no-redact")
|
tapCmdArgs = append(tapCmdArgs, "--no-redact")
|
||||||
@@ -383,7 +400,7 @@ func TestTapNoRedact(t *testing.T) {
|
|||||||
t.Logf("running command: %v", tapCmd.String())
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
if err := cleanupCommand(tapCmd); err != nil {
|
if err := CleanupCommand(tapCmd); err != nil {
|
||||||
t.Logf("failed to cleanup tap command, err: %v", err)
|
t.Logf("failed to cleanup tap command, err: %v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -393,24 +410,24 @@ func TestTapNoRedact(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiServerUrl := getApiServerUrl(defaultApiServerPort)
|
apiServerUrl := GetApiServerUrl(defaultApiServerPort)
|
||||||
|
|
||||||
if err := waitTapPodsReady(apiServerUrl); err != nil {
|
if err := WaitTapPodsReady(apiServerUrl); err != nil {
|
||||||
t.Errorf("failed to start tap pods on time, err: %v", err)
|
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyUrl := getProxyUrl(defaultNamespaceName, defaultServiceName)
|
proxyUrl := GetProxyUrl(defaultNamespaceName, defaultServiceName)
|
||||||
requestHeaders := map[string]string{"User-Header": "Mizu"}
|
requestHeaders := map[string]string{"User-Header": "Mizu"}
|
||||||
requestBody := map[string]string{"User": "Mizu"}
|
requestBody := map[string]string{"User": "Mizu"}
|
||||||
for i := 0; i < defaultEntriesCount; i++ {
|
for i := 0; i < defaultEntriesCount; i++ {
|
||||||
if _, requestErr := executeHttpPostRequestWithHeaders(fmt.Sprintf("%v/post", proxyUrl), requestHeaders, requestBody); requestErr != nil {
|
if _, requestErr := ExecuteHttpPostRequestWithHeaders(fmt.Sprintf("%v/post", proxyUrl), requestHeaders, requestBody); requestErr != nil {
|
||||||
t.Errorf("failed to send proxy request, err: %v", requestErr)
|
t.Errorf("failed to send proxy request, err: %v", requestErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/NoRedact.js\"")
|
RunCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/NoRedact.js\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTapRegexMasking(t *testing.T) {
|
func TestTapRegexMasking(t *testing.T) {
|
||||||
@@ -418,15 +435,15 @@ func TestTapRegexMasking(t *testing.T) {
|
|||||||
t.Skip("ignored acceptance test")
|
t.Skip("ignored acceptance test")
|
||||||
}
|
}
|
||||||
|
|
||||||
cliPath, cliPathErr := getCliPath()
|
cliPath, cliPathErr := GetCliPath()
|
||||||
if cliPathErr != nil {
|
if cliPathErr != nil {
|
||||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tapCmdArgs := getDefaultTapCommandArgs()
|
tapCmdArgs := GetDefaultTapCommandArgs()
|
||||||
|
|
||||||
tapNamespace := getDefaultTapNamespace()
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
tapCmdArgs = append(tapCmdArgs, "-r", "Mizu")
|
tapCmdArgs = append(tapCmdArgs, "-r", "Mizu")
|
||||||
@@ -435,7 +452,7 @@ func TestTapRegexMasking(t *testing.T) {
|
|||||||
t.Logf("running command: %v", tapCmd.String())
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
if err := cleanupCommand(tapCmd); err != nil {
|
if err := CleanupCommand(tapCmd); err != nil {
|
||||||
t.Logf("failed to cleanup tap command, err: %v", err)
|
t.Logf("failed to cleanup tap command, err: %v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -445,23 +462,23 @@ func TestTapRegexMasking(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiServerUrl := getApiServerUrl(defaultApiServerPort)
|
apiServerUrl := GetApiServerUrl(defaultApiServerPort)
|
||||||
|
|
||||||
if err := waitTapPodsReady(apiServerUrl); err != nil {
|
if err := WaitTapPodsReady(apiServerUrl); err != nil {
|
||||||
t.Errorf("failed to start tap pods on time, err: %v", err)
|
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyUrl := getProxyUrl(defaultNamespaceName, defaultServiceName)
|
proxyUrl := GetProxyUrl(defaultNamespaceName, defaultServiceName)
|
||||||
for i := 0; i < defaultEntriesCount; i++ {
|
for i := 0; i < defaultEntriesCount; i++ {
|
||||||
response, requestErr := http.Post(fmt.Sprintf("%v/post", proxyUrl), "text/plain", bytes.NewBufferString("Mizu"))
|
response, requestErr := http.Post(fmt.Sprintf("%v/post", proxyUrl), "text/plain", bytes.NewBufferString("Mizu"))
|
||||||
if _, requestErr = executeHttpRequest(response, requestErr); requestErr != nil {
|
if _, requestErr = ExecuteHttpRequest(response, requestErr); requestErr != nil {
|
||||||
t.Errorf("failed to send proxy request, err: %v", requestErr)
|
t.Errorf("failed to send proxy request, err: %v", requestErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/RegexMasking.js\"")
|
RunCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/RegexMasking.js\"")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,15 +487,15 @@ func TestTapIgnoredUserAgents(t *testing.T) {
|
|||||||
t.Skip("ignored acceptance test")
|
t.Skip("ignored acceptance test")
|
||||||
}
|
}
|
||||||
|
|
||||||
cliPath, cliPathErr := getCliPath()
|
cliPath, cliPathErr := GetCliPath()
|
||||||
if cliPathErr != nil {
|
if cliPathErr != nil {
|
||||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tapCmdArgs := getDefaultTapCommandArgs()
|
tapCmdArgs := GetDefaultTapCommandArgs()
|
||||||
|
|
||||||
tapNamespace := getDefaultTapNamespace()
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
ignoredUserAgentValue := "ignore"
|
ignoredUserAgentValue := "ignore"
|
||||||
@@ -488,7 +505,7 @@ func TestTapIgnoredUserAgents(t *testing.T) {
|
|||||||
t.Logf("running command: %v", tapCmd.String())
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
if err := cleanupCommand(tapCmd); err != nil {
|
if err := CleanupCommand(tapCmd); err != nil {
|
||||||
t.Logf("failed to cleanup tap command, err: %v", err)
|
t.Logf("failed to cleanup tap command, err: %v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -498,32 +515,32 @@ func TestTapIgnoredUserAgents(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiServerUrl := getApiServerUrl(defaultApiServerPort)
|
apiServerUrl := GetApiServerUrl(defaultApiServerPort)
|
||||||
|
|
||||||
if err := waitTapPodsReady(apiServerUrl); err != nil {
|
if err := WaitTapPodsReady(apiServerUrl); err != nil {
|
||||||
t.Errorf("failed to start tap pods on time, err: %v", err)
|
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyUrl := getProxyUrl(defaultNamespaceName, defaultServiceName)
|
proxyUrl := GetProxyUrl(defaultNamespaceName, defaultServiceName)
|
||||||
|
|
||||||
ignoredUserAgentCustomHeader := "Ignored-User-Agent"
|
ignoredUserAgentCustomHeader := "Ignored-User-Agent"
|
||||||
headers := map[string]string{"User-Agent": ignoredUserAgentValue, ignoredUserAgentCustomHeader: ""}
|
headers := map[string]string{"User-Agent": ignoredUserAgentValue, ignoredUserAgentCustomHeader: ""}
|
||||||
for i := 0; i < defaultEntriesCount; i++ {
|
for i := 0; i < defaultEntriesCount; i++ {
|
||||||
if _, requestErr := executeHttpGetRequestWithHeaders(fmt.Sprintf("%v/get", proxyUrl), headers); requestErr != nil {
|
if _, requestErr := ExecuteHttpGetRequestWithHeaders(fmt.Sprintf("%v/get", proxyUrl), headers); requestErr != nil {
|
||||||
t.Errorf("failed to send proxy request, err: %v", requestErr)
|
t.Errorf("failed to send proxy request, err: %v", requestErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < defaultEntriesCount; i++ {
|
for i := 0; i < defaultEntriesCount; i++ {
|
||||||
if _, requestErr := executeHttpGetRequest(fmt.Sprintf("%v/get", proxyUrl)); requestErr != nil {
|
if _, requestErr := ExecuteHttpGetRequest(fmt.Sprintf("%v/get", proxyUrl)); requestErr != nil {
|
||||||
t.Errorf("failed to send proxy request, err: %v", requestErr)
|
t.Errorf("failed to send proxy request, err: %v", requestErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/IgnoredUserAgents.js\"")
|
RunCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/IgnoredUserAgents.js\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTapDumpLogs(t *testing.T) {
|
func TestTapDumpLogs(t *testing.T) {
|
||||||
@@ -531,15 +548,15 @@ func TestTapDumpLogs(t *testing.T) {
|
|||||||
t.Skip("ignored acceptance test")
|
t.Skip("ignored acceptance test")
|
||||||
}
|
}
|
||||||
|
|
||||||
cliPath, cliPathErr := getCliPath()
|
cliPath, cliPathErr := GetCliPath()
|
||||||
if cliPathErr != nil {
|
if cliPathErr != nil {
|
||||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tapCmdArgs := getDefaultTapCommandArgs()
|
tapCmdArgs := GetDefaultTapCommandArgs()
|
||||||
|
|
||||||
tapNamespace := getDefaultTapNamespace()
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
tapCmdArgs = append(tapCmdArgs, "--set", "dump-logs=true")
|
tapCmdArgs = append(tapCmdArgs, "--set", "dump-logs=true")
|
||||||
@@ -552,19 +569,19 @@ func TestTapDumpLogs(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiServerUrl := getApiServerUrl(defaultApiServerPort)
|
apiServerUrl := GetApiServerUrl(defaultApiServerPort)
|
||||||
|
|
||||||
if err := waitTapPodsReady(apiServerUrl); err != nil {
|
if err := WaitTapPodsReady(apiServerUrl); err != nil {
|
||||||
t.Errorf("failed to start tap pods on time, err: %v", err)
|
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cleanupCommand(tapCmd); err != nil {
|
if err := CleanupCommand(tapCmd); err != nil {
|
||||||
t.Errorf("failed to cleanup tap command, err: %v", err)
|
t.Errorf("failed to cleanup tap command, err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mizuFolderPath, mizuPathErr := getMizuFolderPath()
|
mizuFolderPath, mizuPathErr := GetMizuFolderPath()
|
||||||
if mizuPathErr != nil {
|
if mizuPathErr != nil {
|
||||||
t.Errorf("failed to get mizu folder path, err: %v", mizuPathErr)
|
t.Errorf("failed to get mizu folder path, err: %v", mizuPathErr)
|
||||||
return
|
return
|
||||||
@@ -632,3 +649,44 @@ func TestTapDumpLogs(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIpResolving(t *testing.T) {
|
||||||
|
namespace := allNamespaces
|
||||||
|
|
||||||
|
t.Log("add permissions for ip-resolution for current user")
|
||||||
|
if err := ApplyKubeFilesForTest(
|
||||||
|
t,
|
||||||
|
"minikube",
|
||||||
|
namespace,
|
||||||
|
"../cli/cmd/permissionFiles/permissions-all-namespaces-ip-resolution-optional.yaml",
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to create k8s permissions, %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
basicTapTest(t, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRestrictedMode(t *testing.T) {
|
||||||
|
namespace := "mizu-tests"
|
||||||
|
|
||||||
|
t.Log("creating permissions for restricted user")
|
||||||
|
if err := ApplyKubeFilesForTest(
|
||||||
|
t,
|
||||||
|
"minikube",
|
||||||
|
namespace,
|
||||||
|
"../cli/cmd/permissionFiles/permissions-ns-tap.yaml",
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to create k8s permissions, %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("switching k8s context to user")
|
||||||
|
if err := SwitchKubeContextForTest(t, "user-with-restricted-access"); err != nil {
|
||||||
|
t.Errorf("failed to switch k8s context, %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
extraArgs := []string{"--set", fmt.Sprintf("mizu-resources-namespace=%s", namespace)}
|
||||||
|
t.Run("basic tap", func (testingT *testing.T) {basicTapTest(testingT, false, extraArgs...)})
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const (
|
|||||||
defaultServiceName = "httpbin"
|
defaultServiceName = "httpbin"
|
||||||
defaultEntriesCount = 50
|
defaultEntriesCount = 50
|
||||||
waitAfterTapPodsReady = 3 * time.Second
|
waitAfterTapPodsReady = 3 * time.Second
|
||||||
|
allNamespaces = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
type PodDescriptor struct {
|
type PodDescriptor struct {
|
||||||
@@ -38,7 +39,7 @@ type PodDescriptor struct {
|
|||||||
Namespace string
|
Namespace string
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCliPath() (string, error) {
|
func GetCliPath() (string, error) {
|
||||||
dir, filePathErr := os.Getwd()
|
dir, filePathErr := os.Getwd()
|
||||||
if filePathErr != nil {
|
if filePathErr != nil {
|
||||||
return "", filePathErr
|
return "", filePathErr
|
||||||
@@ -48,7 +49,7 @@ func getCliPath() (string, error) {
|
|||||||
return cliPath, nil
|
return cliPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMizuFolderPath() (string, error) {
|
func GetMizuFolderPath() (string, error) {
|
||||||
home, homeDirErr := os.UserHomeDir()
|
home, homeDirErr := os.UserHomeDir()
|
||||||
if homeDirErr != nil {
|
if homeDirErr != nil {
|
||||||
return "", homeDirErr
|
return "", homeDirErr
|
||||||
@@ -57,8 +58,8 @@ func getMizuFolderPath() (string, error) {
|
|||||||
return path.Join(home, ".mizu"), nil
|
return path.Join(home, ".mizu"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getConfigPath() (string, error) {
|
func GetConfigPath() (string, error) {
|
||||||
mizuFolderPath, mizuPathError := getMizuFolderPath()
|
mizuFolderPath, mizuPathError := GetMizuFolderPath()
|
||||||
if mizuPathError != nil {
|
if mizuPathError != nil {
|
||||||
return "", mizuPathError
|
return "", mizuPathError
|
||||||
}
|
}
|
||||||
@@ -66,15 +67,15 @@ func getConfigPath() (string, error) {
|
|||||||
return path.Join(mizuFolderPath, "config.yaml"), nil
|
return path.Join(mizuFolderPath, "config.yaml"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getProxyUrl(namespace string, service string) string {
|
func GetProxyUrl(namespace string, service string) string {
|
||||||
return fmt.Sprintf("http://localhost:8080/api/v1/namespaces/%v/services/%v/proxy", namespace, service)
|
return fmt.Sprintf("http://localhost:8080/api/v1/namespaces/%v/services/%v/proxy", namespace, service)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getApiServerUrl(port uint16) string {
|
func GetApiServerUrl(port uint16) string {
|
||||||
return fmt.Sprintf("http://localhost:%v", port)
|
return fmt.Sprintf("http://localhost:%v", port)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getServiceExternalIp(ctx context.Context, namespace string, service string) (string, error) {
|
func NewKubernetesProvider() (*KubernetesProvider, error) {
|
||||||
home := homedir.HomeDir()
|
home := homedir.HomeDir()
|
||||||
configLoadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: filepath.Join(home, ".kube", "config")}
|
configLoadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: filepath.Join(home, ".kube", "config")}
|
||||||
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
|
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
|
||||||
@@ -86,15 +87,23 @@ func getServiceExternalIp(ctx context.Context, namespace string, service string)
|
|||||||
|
|
||||||
restClientConfig, err := clientConfig.ClientConfig()
|
restClientConfig, err := clientConfig.ClientConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
clientSet, err := kubernetes.NewForConfig(restClientConfig)
|
clientSet, err := kubernetes.NewForConfig(restClientConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceObj, err := clientSet.CoreV1().Services(namespace).Get(ctx, service, metav1.GetOptions{})
|
return &KubernetesProvider{clientSet}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type KubernetesProvider struct {
|
||||||
|
clientSet *kubernetes.Clientset
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kp *KubernetesProvider) GetServiceExternalIp(ctx context.Context, namespace string, service string) (string, error) {
|
||||||
|
serviceObj, err := kp.clientSet.CoreV1().Services(namespace).Get(ctx, service, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -103,6 +112,105 @@ func getServiceExternalIp(ctx context.Context, namespace string, service string)
|
|||||||
return externalIp, nil
|
return externalIp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SwitchKubeContextForTest(t *testing.T, newContextName string) error {
|
||||||
|
prevKubeContextName, err := GetKubeCurrentContextName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := SetKubeCurrentContext(newContextName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err := SetKubeCurrentContext(prevKubeContextName); err != nil {
|
||||||
|
t.Errorf("failed to set Kubernetes context to %s, err: %v", prevKubeContextName, err)
|
||||||
|
t.Errorf("cleanup failed, subsequent tests may be affected")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetKubeCurrentContextName() (string, error) {
|
||||||
|
cmd := exec.Command("kubectl", "config", "current-context")
|
||||||
|
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("%v, %s", err, string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(bytes.TrimSpace(output)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetKubeCurrentContext(contextName string) error {
|
||||||
|
cmd := exec.Command("kubectl", "config", "use-context", contextName)
|
||||||
|
|
||||||
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
return fmt.Errorf("%v, %s", err, string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplyKubeFilesForTest(t *testing.T, kubeContext string, namespace string, filename ...string) error {
|
||||||
|
for i := range filename {
|
||||||
|
fname := filename[i]
|
||||||
|
if err := ApplyKubeFile(kubeContext, namespace, fname); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err := DeleteKubeFile(kubeContext, namespace, fname); err != nil {
|
||||||
|
t.Errorf(
|
||||||
|
"failed to delete Kubernetes resources in namespace %s from filename %s, err: %v",
|
||||||
|
namespace,
|
||||||
|
fname,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplyKubeFile(kubeContext string, namespace string, filename string) (error) {
|
||||||
|
cmdArgs := []string{
|
||||||
|
"apply",
|
||||||
|
"--context", kubeContext,
|
||||||
|
"-f", filename,
|
||||||
|
}
|
||||||
|
if namespace != allNamespaces {
|
||||||
|
cmdArgs = append(cmdArgs, "-n", namespace)
|
||||||
|
}
|
||||||
|
cmd := exec.Command("kubectl", cmdArgs...)
|
||||||
|
|
||||||
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
return fmt.Errorf("%v, %s", err, string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteKubeFile(kubeContext string, namespace string, filename string) error {
|
||||||
|
cmdArgs := []string{
|
||||||
|
"delete",
|
||||||
|
"--context", kubeContext,
|
||||||
|
"-f", filename,
|
||||||
|
}
|
||||||
|
if namespace != allNamespaces {
|
||||||
|
cmdArgs = append(cmdArgs, "-n", namespace)
|
||||||
|
}
|
||||||
|
cmd := exec.Command("kubectl", cmdArgs...)
|
||||||
|
|
||||||
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
return fmt.Errorf("%v, %s", err, string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func getDefaultCommandArgs() []string {
|
func getDefaultCommandArgs() []string {
|
||||||
setFlag := "--set"
|
setFlag := "--set"
|
||||||
telemetry := "telemetry=false"
|
telemetry := "telemetry=false"
|
||||||
@@ -113,46 +221,47 @@ func getDefaultCommandArgs() []string {
|
|||||||
return []string{setFlag, telemetry, setFlag, agentImage, setFlag, imagePullPolicy, setFlag, headless}
|
return []string{setFlag, telemetry, setFlag, agentImage, setFlag, imagePullPolicy, setFlag, headless}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultTapCommandArgs() []string {
|
func GetDefaultTapCommandArgs() []string {
|
||||||
tapCommand := "tap"
|
tapCommand := "tap"
|
||||||
defaultCmdArgs := getDefaultCommandArgs()
|
defaultCmdArgs := getDefaultCommandArgs()
|
||||||
|
|
||||||
return append([]string{tapCommand}, defaultCmdArgs...)
|
return append([]string{tapCommand}, defaultCmdArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultTapCommandArgsWithRegex(regex string) []string {
|
func GetDefaultTapCommandArgsWithRegex(regex string) []string {
|
||||||
tapCommand := "tap"
|
tapCommand := "tap"
|
||||||
defaultCmdArgs := getDefaultCommandArgs()
|
defaultCmdArgs := getDefaultCommandArgs()
|
||||||
|
|
||||||
return append([]string{tapCommand, regex}, defaultCmdArgs...)
|
return append([]string{tapCommand, regex}, defaultCmdArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultLogsCommandArgs() []string {
|
func GetDefaultLogsCommandArgs() []string {
|
||||||
logsCommand := "logs"
|
logsCommand := "logs"
|
||||||
defaultCmdArgs := getDefaultCommandArgs()
|
defaultCmdArgs := getDefaultCommandArgs()
|
||||||
|
|
||||||
return append([]string{logsCommand}, defaultCmdArgs...)
|
return append([]string{logsCommand}, defaultCmdArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultTapNamespace() []string {
|
func GetDefaultTapNamespace() []string {
|
||||||
return []string{"-n", "mizu-tests"}
|
return []string{"-n", "mizu-tests"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultConfigCommandArgs() []string {
|
func GetDefaultConfigCommandArgs() []string {
|
||||||
configCommand := "config"
|
configCommand := "config"
|
||||||
defaultCmdArgs := getDefaultCommandArgs()
|
defaultCmdArgs := getDefaultCommandArgs()
|
||||||
|
|
||||||
return append([]string{configCommand}, defaultCmdArgs...)
|
return append([]string{configCommand}, defaultCmdArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCypressTests(t *testing.T, cypressRunCmd string) {
|
func RunCypressTests(t *testing.T, cypressRunCmd string) {
|
||||||
cypressCmd := exec.Command("bash", "-c", cypressRunCmd)
|
cypressCmd := exec.Command("bash", "-c", cypressRunCmd)
|
||||||
t.Logf("running command: %v", cypressCmd.String())
|
t.Logf("running command: %v", cypressCmd.String())
|
||||||
out, err := cypressCmd.Output()
|
out, err := cypressCmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%s", out)
|
t.Errorf("error running cypress, error: %v, output: %v", err, string(out))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("%s", out)
|
t.Logf("%s", out)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,10 +292,10 @@ func tryExecuteFunc(executeFunc func() error) (err interface{}) {
|
|||||||
return executeFunc()
|
return executeFunc()
|
||||||
}
|
}
|
||||||
|
|
||||||
func waitTapPodsReady(apiServerUrl string) error {
|
func WaitTapPodsReady(apiServerUrl string) error {
|
||||||
resolvingUrl := fmt.Sprintf("%v/status/connectedTappersCount", apiServerUrl)
|
resolvingUrl := fmt.Sprintf("%v/status/connectedTappersCount", apiServerUrl)
|
||||||
tapPodsReadyFunc := func() error {
|
tapPodsReadyFunc := func() error {
|
||||||
requestResult, requestErr := executeHttpGetRequest(resolvingUrl)
|
requestResult, requestErr := ExecuteHttpGetRequest(resolvingUrl)
|
||||||
if requestErr != nil {
|
if requestErr != nil {
|
||||||
return requestErr
|
return requestErr
|
||||||
}
|
}
|
||||||
@@ -211,7 +320,7 @@ func jsonBytesToInterface(jsonBytes []byte) (interface{}, error) {
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func executeHttpRequest(response *http.Response, requestErr error) (interface{}, error) {
|
func ExecuteHttpRequest(response *http.Response, requestErr error) (interface{}, error) {
|
||||||
if requestErr != nil {
|
if requestErr != nil {
|
||||||
return nil, requestErr
|
return nil, requestErr
|
||||||
} else if response.StatusCode != 200 {
|
} else if response.StatusCode != 200 {
|
||||||
@@ -228,7 +337,7 @@ func executeHttpRequest(response *http.Response, requestErr error) (interface{},
|
|||||||
return jsonBytesToInterface(data)
|
return jsonBytesToInterface(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func executeHttpGetRequestWithHeaders(url string, headers map[string]string) (interface{}, error) {
|
func ExecuteHttpGetRequestWithHeaders(url string, headers map[string]string) (interface{}, error) {
|
||||||
request, err := http.NewRequest(http.MethodGet, url, nil)
|
request, err := http.NewRequest(http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -240,15 +349,15 @@ func executeHttpGetRequestWithHeaders(url string, headers map[string]string) (in
|
|||||||
|
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
response, requestErr := client.Do(request)
|
response, requestErr := client.Do(request)
|
||||||
return executeHttpRequest(response, requestErr)
|
return ExecuteHttpRequest(response, requestErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func executeHttpGetRequest(url string) (interface{}, error) {
|
func ExecuteHttpGetRequest(url string) (interface{}, error) {
|
||||||
response, requestErr := http.Get(url)
|
response, requestErr := http.Get(url)
|
||||||
return executeHttpRequest(response, requestErr)
|
return ExecuteHttpRequest(response, requestErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func executeHttpPostRequestWithHeaders(url string, headers map[string]string, body interface{}) (interface{}, error) {
|
func ExecuteHttpPostRequestWithHeaders(url string, headers map[string]string, body interface{}) (interface{}, error) {
|
||||||
requestBody, jsonErr := json.Marshal(body)
|
requestBody, jsonErr := json.Marshal(body)
|
||||||
if jsonErr != nil {
|
if jsonErr != nil {
|
||||||
return nil, jsonErr
|
return nil, jsonErr
|
||||||
@@ -266,10 +375,10 @@ func executeHttpPostRequestWithHeaders(url string, headers map[string]string, bo
|
|||||||
|
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
response, requestErr := client.Do(request)
|
response, requestErr := client.Do(request)
|
||||||
return executeHttpRequest(response, requestErr)
|
return ExecuteHttpRequest(response, requestErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cleanupCommand(cmd *exec.Cmd) error {
|
func CleanupCommand(cmd *exec.Cmd) error {
|
||||||
if err := cmd.Process.Signal(syscall.SIGQUIT); err != nil {
|
if err := cmd.Process.Signal(syscall.SIGQUIT); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -281,7 +390,7 @@ func cleanupCommand(cmd *exec.Cmd) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLogsPath() (string, error) {
|
func GetLogsPath() (string, error) {
|
||||||
dir, filePathErr := os.Getwd()
|
dir, filePathErr := os.Getwd()
|
||||||
if filePathErr != nil {
|
if filePathErr != nil {
|
||||||
return "", filePathErr
|
return "", filePathErr
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ go 1.17
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/antelman107/net-wait-go v0.0.0-20210623112055-cf684aebda7b
|
github.com/antelman107/net-wait-go v0.0.0-20210623112055-cf684aebda7b
|
||||||
github.com/chanced/openapi v0.0.7
|
github.com/chanced/openapi v0.0.8
|
||||||
github.com/djherbis/atime v1.1.0
|
github.com/djherbis/atime v1.1.0
|
||||||
github.com/elastic/go-elasticsearch/v7 v7.17.0
|
github.com/elastic/go-elasticsearch/v7 v7.17.0
|
||||||
github.com/getkin/kin-openapi v0.89.0
|
github.com/getkin/kin-openapi v0.89.0
|
||||||
@@ -22,7 +22,7 @@ require (
|
|||||||
github.com/ory/kratos-client-go v0.8.2-alpha.1
|
github.com/ory/kratos-client-go v0.8.2-alpha.1
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/up9inc/basenine/client/go v0.0.0-20220125035757-926e42208705
|
github.com/up9inc/basenine/client/go v0.0.0-20220220204122-0ef8cb24fab1
|
||||||
github.com/up9inc/mizu/shared v0.0.0
|
github.com/up9inc/mizu/shared v0.0.0
|
||||||
github.com/up9inc/mizu/tap v0.0.0
|
github.com/up9inc/mizu/tap v0.0.0
|
||||||
github.com/up9inc/mizu/tap/api v0.0.0
|
github.com/up9inc/mizu/tap/api v0.0.0
|
||||||
@@ -54,6 +54,7 @@ require (
|
|||||||
github.com/bradleyfalzon/tlsx v0.0.0-20170624122154-28fd0e59bac4 // indirect
|
github.com/bradleyfalzon/tlsx v0.0.0-20170624122154-28fd0e59bac4 // indirect
|
||||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
|
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
|
||||||
github.com/chanced/dynamic v0.0.0-20211210164248-f8fadb1d735b // indirect
|
github.com/chanced/dynamic v0.0.0-20211210164248-f8fadb1d735b // indirect
|
||||||
|
github.com/cilium/ebpf v0.8.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||||
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
|
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
|
||||||
|
|||||||
14
agent/go.sum
14
agent/go.sum
@@ -132,11 +132,13 @@ github.com/chanced/cmpjson v0.0.0-20210415035445-da9262c1f20a/go.mod h1:yhcmlFk1
|
|||||||
github.com/chanced/dynamic v0.0.0-20210502140838-c010b5fc3e44/go.mod h1:XVNfXN5kgZST4PQ0W/oBAHJku2OteCeHxjAbvfd0ARM=
|
github.com/chanced/dynamic v0.0.0-20210502140838-c010b5fc3e44/go.mod h1:XVNfXN5kgZST4PQ0W/oBAHJku2OteCeHxjAbvfd0ARM=
|
||||||
github.com/chanced/dynamic v0.0.0-20211210164248-f8fadb1d735b h1:nQWfVfhByCAYUjDxWNMyMtq3VZ8AGOxF7wZlDnC5cTc=
|
github.com/chanced/dynamic v0.0.0-20211210164248-f8fadb1d735b h1:nQWfVfhByCAYUjDxWNMyMtq3VZ8AGOxF7wZlDnC5cTc=
|
||||||
github.com/chanced/dynamic v0.0.0-20211210164248-f8fadb1d735b/go.mod h1:XVNfXN5kgZST4PQ0W/oBAHJku2OteCeHxjAbvfd0ARM=
|
github.com/chanced/dynamic v0.0.0-20211210164248-f8fadb1d735b/go.mod h1:XVNfXN5kgZST4PQ0W/oBAHJku2OteCeHxjAbvfd0ARM=
|
||||||
github.com/chanced/openapi v0.0.7 h1:OmOBHCg/5ViUg0gaGxXBeEFoVBE8C2pHK4BO/AiD6k8=
|
github.com/chanced/openapi v0.0.8 h1:pOqKTvZEET2odGE+kJBrAdXvgpTKFPk+XRz5NTuMvrM=
|
||||||
github.com/chanced/openapi v0.0.7/go.mod h1:SxE2VMLPw+T7Vq8nwbVVhDF2PigvRF4n5XyqsVpRJGU=
|
github.com/chanced/openapi v0.0.8/go.mod h1:SxE2VMLPw+T7Vq8nwbVVhDF2PigvRF4n5XyqsVpRJGU=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/cilium/ebpf v0.8.0 h1:2V6KSg3FRADVU2BMIRemZ0hV+9OM+aAHhZDjQyjJTAs=
|
||||||
|
github.com/cilium/ebpf v0.8.0/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk=
|
||||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
@@ -210,8 +212,9 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF
|
|||||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||||
github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY=
|
|
||||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||||
|
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
|
||||||
|
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||||
@@ -850,8 +853,8 @@ github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn
|
|||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
|
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
|
||||||
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
|
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
|
||||||
github.com/up9inc/basenine/client/go v0.0.0-20220125035757-926e42208705 h1:5LLhzv0cjb/F+dU0z3j8teVGjQInMYAocTyAZohKUwY=
|
github.com/up9inc/basenine/client/go v0.0.0-20220220204122-0ef8cb24fab1 h1:0XN8s3HtwUBr9hbWRAFulFMsu1f2cabfJbwpz/sOoLA=
|
||||||
github.com/up9inc/basenine/client/go v0.0.0-20220125035757-926e42208705/go.mod h1:SvJGPoa/6erhUQV7kvHBwM/0x5LyO6XaG2lUaCaKiUI=
|
github.com/up9inc/basenine/client/go v0.0.0-20220220204122-0ef8cb24fab1/go.mod h1:SvJGPoa/6erhUQV7kvHBwM/0x5LyO6XaG2lUaCaKiUI=
|
||||||
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
|
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
@@ -1158,6 +1161,7 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
|||||||
@@ -56,11 +56,9 @@ const (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
logLevel := determineLogLevel()
|
logLevel := determineLogLevel()
|
||||||
logger.InitLoggerStderrOnly(logLevel)
|
logger.InitLoggerStd(logLevel)
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if err := config.LoadConfig(); err != nil {
|
|
||||||
logger.Log.Fatalf("Error loading config file %v", err)
|
|
||||||
}
|
|
||||||
app.LoadExtensions()
|
app.LoadExtensions()
|
||||||
|
|
||||||
if !*tapperMode && !*apiServerMode && !*standaloneMode && !*harsReaderMode {
|
if !*tapperMode && !*apiServerMode && !*standaloneMode && !*harsReaderMode {
|
||||||
@@ -139,7 +137,10 @@ func hostApi(socketHarOutputChannel chan<- *tapApi.OutputChannelItem) *gin.Engin
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runInApiServerMode(namespace string) *gin.Engine {
|
func runInApiServerMode(namespace string) *gin.Engine {
|
||||||
app.ConfigureBasenineServer(shared.BasenineHost, shared.BaseninePort)
|
if err := config.LoadConfig(); err != nil {
|
||||||
|
logger.Log.Fatalf("Error loading config file %v", err)
|
||||||
|
}
|
||||||
|
app.ConfigureBasenineServer(shared.BasenineHost, shared.BaseninePort, config.Config.MaxDBSizeBytes, config.Config.LogLevel)
|
||||||
startTime = time.Now().UnixNano() / int64(time.Millisecond)
|
startTime = time.Now().UnixNano() / int64(time.Millisecond)
|
||||||
api.StartResolving(namespace)
|
api.StartResolving(namespace)
|
||||||
|
|
||||||
@@ -215,7 +216,7 @@ func enableExpFeatureIfNeeded() {
|
|||||||
oas.GetOasGeneratorInstance().Start()
|
oas.GetOasGeneratorInstance().Start()
|
||||||
}
|
}
|
||||||
if config.Config.ServiceMap {
|
if config.Config.ServiceMap {
|
||||||
servicemap.GetInstance().SetConfig(config.Config)
|
servicemap.GetInstance().Enable()
|
||||||
}
|
}
|
||||||
elastic.GetInstance().Configure(config.Config.Elastic)
|
elastic.GetInstance().Configure(config.Config.Elastic)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,8 +118,8 @@ func startReadingChannel(outputItems <-chan *tapApi.OutputChannelItem, extension
|
|||||||
|
|
||||||
for item := range outputItems {
|
for item := range outputItems {
|
||||||
extension := extensionsMap[item.Protocol.Name]
|
extension := extensionsMap[item.Protocol.Name]
|
||||||
resolvedSource, resolvedDestionation := resolveIP(item.ConnectionInfo)
|
resolvedSource, resolvedDestionation, namespace := resolveIP(item.ConnectionInfo)
|
||||||
mizuEntry := extension.Dissector.Analyze(item, resolvedSource, resolvedDestionation)
|
mizuEntry := extension.Dissector.Analyze(item, resolvedSource, resolvedDestionation, namespace)
|
||||||
if extension.Protocol.Name == "http" {
|
if extension.Protocol.Name == "http" {
|
||||||
if !disableOASValidation {
|
if !disableOASValidation {
|
||||||
var httpPair tapApi.HTTPRequestResponsePair
|
var httpPair tapApi.HTTPRequestResponsePair
|
||||||
@@ -140,7 +140,17 @@ func startReadingChannel(outputItems <-chan *tapApi.OutputChannelItem, extension
|
|||||||
mizuEntry.Rules = rules
|
mizuEntry.Rules = rules
|
||||||
}
|
}
|
||||||
|
|
||||||
entryWSource := oas.EntryWithSource{Entry: *harEntry, Source: mizuEntry.Source.Name, Id: mizuEntry.Id}
|
entryWSource := oas.EntryWithSource{
|
||||||
|
Entry: *harEntry,
|
||||||
|
Source: mizuEntry.Source.Name,
|
||||||
|
Destination: mizuEntry.Destination.Name,
|
||||||
|
Id: mizuEntry.Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
if entryWSource.Destination == "" {
|
||||||
|
entryWSource.Destination = mizuEntry.Destination.IP + ":" + mizuEntry.Destination.Port
|
||||||
|
}
|
||||||
|
|
||||||
oas.GetOasGeneratorInstance().PushEntry(&entryWSource)
|
oas.GetOasGeneratorInstance().PushEntry(&entryWSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,26 +168,32 @@ func startReadingChannel(outputItems <-chan *tapApi.OutputChannelItem, extension
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveIP(connectionInfo *tapApi.ConnectionInfo) (resolvedSource string, resolvedDestination string) {
|
func resolveIP(connectionInfo *tapApi.ConnectionInfo) (resolvedSource string, resolvedDestination string, namespace string) {
|
||||||
if k8sResolver != nil {
|
if k8sResolver != nil {
|
||||||
unresolvedSource := connectionInfo.ClientIP
|
unresolvedSource := connectionInfo.ClientIP
|
||||||
resolvedSource = k8sResolver.Resolve(unresolvedSource)
|
resolvedSourceObject := k8sResolver.Resolve(unresolvedSource)
|
||||||
if resolvedSource == "" {
|
if resolvedSourceObject == nil {
|
||||||
logger.Log.Debugf("Cannot find resolved name to source: %s", unresolvedSource)
|
logger.Log.Debugf("Cannot find resolved name to source: %s", unresolvedSource)
|
||||||
if os.Getenv("SKIP_NOT_RESOLVED_SOURCE") == "1" {
|
if os.Getenv("SKIP_NOT_RESOLVED_SOURCE") == "1" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
resolvedSource = resolvedSourceObject.FullAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
unresolvedDestination := fmt.Sprintf("%s:%s", connectionInfo.ServerIP, connectionInfo.ServerPort)
|
unresolvedDestination := fmt.Sprintf("%s:%s", connectionInfo.ServerIP, connectionInfo.ServerPort)
|
||||||
resolvedDestination = k8sResolver.Resolve(unresolvedDestination)
|
resolvedDestinationObject := k8sResolver.Resolve(unresolvedDestination)
|
||||||
if resolvedDestination == "" {
|
if resolvedDestinationObject == nil {
|
||||||
logger.Log.Debugf("Cannot find resolved name to dest: %s", unresolvedDestination)
|
logger.Log.Debugf("Cannot find resolved name to dest: %s", unresolvedDestination)
|
||||||
if os.Getenv("SKIP_NOT_RESOLVED_DEST") == "1" {
|
if os.Getenv("SKIP_NOT_RESOLVED_DEST") == "1" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
resolvedDestination = resolvedDestinationObject.FullAddress
|
||||||
|
namespace = resolvedDestinationObject.Namespace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return resolvedSource, resolvedDestination
|
return resolvedSource, resolvedDestination, namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckIsServiceIP(address string) bool {
|
func CheckIsServiceIP(address string) bool {
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ type SocketConnection struct {
|
|||||||
isTapper bool
|
isTapper bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WebSocketParams struct {
|
||||||
|
Query string `json:"query"`
|
||||||
|
EnableFullEntries bool `json:"enableFullEntries"`
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
websocketUpgrader = websocket.Upgrader{
|
websocketUpgrader = websocket.Upgrader{
|
||||||
ReadBufferSize: 1024,
|
ReadBufferSize: 1024,
|
||||||
@@ -110,6 +115,8 @@ func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers Even
|
|||||||
logger.Log.Error(err)
|
logger.Log.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var params WebSocketParams
|
||||||
|
|
||||||
for {
|
for {
|
||||||
_, msg, err := ws.ReadMessage()
|
_, msg, err := ws.ReadMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -123,7 +130,11 @@ func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers Even
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !isTapper && !isQuerySet {
|
if !isTapper && !isQuerySet {
|
||||||
query := string(msg)
|
if err := json.Unmarshal(msg, ¶ms); err != nil {
|
||||||
|
logger.Log.Errorf("Error: %v", socketId, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
query := params.Query
|
||||||
err = basenine.Validate(shared.BasenineHost, shared.BaseninePort, query)
|
err = basenine.Validate(shared.BasenineHost, shared.BaseninePort, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
toastBytes, _ := models.CreateWebsocketToastMessage(&models.ToastMessage{
|
toastBytes, _ := models.CreateWebsocketToastMessage(&models.ToastMessage{
|
||||||
@@ -150,10 +161,15 @@ func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers Even
|
|||||||
var entry *tapApi.Entry
|
var entry *tapApi.Entry
|
||||||
err = json.Unmarshal(bytes, &entry)
|
err = json.Unmarshal(bytes, &entry)
|
||||||
|
|
||||||
base := tapApi.Summarize(entry)
|
var message []byte
|
||||||
|
if params.EnableFullEntries {
|
||||||
|
message, _ = models.CreateFullEntryWebSocketMessage(entry)
|
||||||
|
} else {
|
||||||
|
base := tapApi.Summarize(entry)
|
||||||
|
message, _ = models.CreateBaseEntryWebSocketMessage(base)
|
||||||
|
}
|
||||||
|
|
||||||
baseEntryBytes, _ := models.CreateBaseEntryWebSocketMessage(base)
|
if err := SendToSocket(socketId, message); err != nil {
|
||||||
if err := SendToSocket(socketId, baseEntryBytes); err != nil {
|
|
||||||
logger.Log.Error(err)
|
logger.Log.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,9 +104,9 @@ func (h *RoutesEventHandlers) WebSocketMessage(_ int, message []byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleTLSLink(outboundLinkMessage models.WebsocketOutboundLinkMessage) {
|
func handleTLSLink(outboundLinkMessage models.WebsocketOutboundLinkMessage) {
|
||||||
resolvedName := k8sResolver.Resolve(outboundLinkMessage.Data.DstIP)
|
resolvedNameObject := k8sResolver.Resolve(outboundLinkMessage.Data.DstIP)
|
||||||
if resolvedName != "" {
|
if resolvedNameObject != nil {
|
||||||
outboundLinkMessage.Data.DstIP = resolvedName
|
outboundLinkMessage.Data.DstIP = resolvedNameObject.FullAddress
|
||||||
} else if outboundLinkMessage.Data.SuggestedResolvedName != "" {
|
} else if outboundLinkMessage.Data.SuggestedResolvedName != "" {
|
||||||
outboundLinkMessage.Data.DstIP = outboundLinkMessage.Data.SuggestedResolvedName
|
outboundLinkMessage.Data.DstIP = outboundLinkMessage.Data.SuggestedResolvedName
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/op/go-logging"
|
"github.com/op/go-logging"
|
||||||
basenine "github.com/up9inc/basenine/client/go"
|
basenine "github.com/up9inc/basenine/client/go"
|
||||||
"github.com/up9inc/mizu/agent/pkg/api"
|
"github.com/up9inc/mizu/agent/pkg/api"
|
||||||
"github.com/up9inc/mizu/agent/pkg/config"
|
|
||||||
"github.com/up9inc/mizu/agent/pkg/controllers"
|
"github.com/up9inc/mizu/agent/pkg/controllers"
|
||||||
"github.com/up9inc/mizu/shared/logger"
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
tapApi "github.com/up9inc/mizu/tap/api"
|
tapApi "github.com/up9inc/mizu/tap/api"
|
||||||
@@ -60,27 +59,21 @@ func LoadExtensions() {
|
|||||||
return Extensions[i].Protocol.Priority < Extensions[j].Protocol.Priority
|
return Extensions[i].Protocol.Priority < Extensions[j].Protocol.Priority
|
||||||
})
|
})
|
||||||
|
|
||||||
for _, extension := range Extensions {
|
|
||||||
logger.Log.Infof("Extension Properties: %+v", extension)
|
|
||||||
}
|
|
||||||
|
|
||||||
controllers.InitExtensionsMap(ExtensionsMap)
|
controllers.InitExtensionsMap(ExtensionsMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConfigureBasenineServer(host string, port string) {
|
func ConfigureBasenineServer(host string, port string, dbSize int64, logLevel logging.Level) {
|
||||||
if !wait.New(
|
if !wait.New(
|
||||||
wait.WithProto("tcp"),
|
wait.WithProto("tcp"),
|
||||||
wait.WithWait(200*time.Millisecond),
|
wait.WithWait(200*time.Millisecond),
|
||||||
wait.WithBreak(50*time.Millisecond),
|
wait.WithBreak(50*time.Millisecond),
|
||||||
wait.WithDeadline(5*time.Second),
|
wait.WithDeadline(5*time.Second),
|
||||||
wait.WithDebug(config.Config.LogLevel == logging.DEBUG),
|
wait.WithDebug(logLevel == logging.DEBUG),
|
||||||
).Do([]string{fmt.Sprintf("%s:%s", host, port)}) {
|
).Do([]string{fmt.Sprintf("%s:%s", host, port)}) {
|
||||||
logger.Log.Panicf("Basenine is not available!")
|
logger.Log.Panicf("Basenine is not available!")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit the database size to default 200MB
|
if err := basenine.Limit(host, port, dbSize); err != nil {
|
||||||
err := basenine.Limit(host, port, config.Config.MaxDBSizeBytes)
|
|
||||||
if err != nil {
|
|
||||||
logger.Log.Panicf("Error while limiting database size: %v", err)
|
logger.Log.Panicf("Error while limiting database size: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,8 +81,7 @@ func ConfigureBasenineServer(host string, port string) {
|
|||||||
for _, extension := range Extensions {
|
for _, extension := range Extensions {
|
||||||
macros := extension.Dissector.Macros()
|
macros := extension.Dissector.Macros()
|
||||||
for macro, expanded := range macros {
|
for macro, expanded := range macros {
|
||||||
err = basenine.Macro(host, port, macro, expanded)
|
if err := basenine.Macro(host, port, macro, expanded); err != nil {
|
||||||
if err != nil {
|
|
||||||
logger.Log.Panicf("Error while adding a macro: %v", err)
|
logger.Log.Panicf("Error while adding a macro: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ func PostTapConfig(c *gin.Context) {
|
|||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
if _, err := startMizuTapperSyncer(ctx, kubernetesProvider, tappedNamespaces, *podRegex, []string{}, tapApi.TrafficFilteringOptions{}, false); err != nil {
|
if _, err := startMizuTapperSyncer(ctx, kubernetesProvider, tappedNamespaces, *podRegex, []string{}, tapApi.TrafficFilteringOptions{}, false, false); err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, err)
|
c.JSON(http.StatusInternalServerError, err)
|
||||||
cancel()
|
cancel()
|
||||||
return
|
return
|
||||||
@@ -100,7 +100,7 @@ func GetTapConfig(c *gin.Context) {
|
|||||||
c.JSON(http.StatusOK, tapConfigToReturn)
|
c.JSON(http.StatusOK, tapConfigToReturn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func startMizuTapperSyncer(ctx context.Context, provider *kubernetes.Provider, targetNamespaces []string, podFilterRegex regexp.Regexp, ignoredUserAgents []string, mizuApiFilteringOptions tapApi.TrafficFilteringOptions, serviceMesh bool) (*kubernetes.MizuTapperSyncer, error) {
|
func startMizuTapperSyncer(ctx context.Context, provider *kubernetes.Provider, targetNamespaces []string, podFilterRegex regexp.Regexp, ignoredUserAgents []string, mizuApiFilteringOptions tapApi.TrafficFilteringOptions, serviceMesh bool, tls bool) (*kubernetes.MizuTapperSyncer, error) {
|
||||||
tapperSyncer, err := kubernetes.CreateAndStartMizuTapperSyncer(ctx, provider, kubernetes.TapperSyncerConfig{
|
tapperSyncer, err := kubernetes.CreateAndStartMizuTapperSyncer(ctx, provider, kubernetes.TapperSyncerConfig{
|
||||||
TargetNamespaces: targetNamespaces,
|
TargetNamespaces: targetNamespaces,
|
||||||
PodFilterRegex: podFilterRegex,
|
PodFilterRegex: podFilterRegex,
|
||||||
@@ -113,6 +113,7 @@ func startMizuTapperSyncer(ctx context.Context, provider *kubernetes.Provider, t
|
|||||||
MizuApiFilteringOptions: mizuApiFilteringOptions,
|
MizuApiFilteringOptions: mizuApiFilteringOptions,
|
||||||
MizuServiceAccountExists: true, //assume service account exists since install mode will not function without it anyway
|
MizuServiceAccountExists: true, //assume service account exists since install mode will not function without it anyway
|
||||||
ServiceMesh: serviceMesh,
|
ServiceMesh: serviceMesh,
|
||||||
|
Tls: tls,
|
||||||
}, time.Now())
|
}, time.Now())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/up9inc/mizu/shared"
|
|
||||||
tapApi "github.com/up9inc/mizu/tap/api"
|
tapApi "github.com/up9inc/mizu/tap/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -59,9 +58,7 @@ type ServiceMapControllerSuite struct {
|
|||||||
|
|
||||||
func (s *ServiceMapControllerSuite) SetupTest() {
|
func (s *ServiceMapControllerSuite) SetupTest() {
|
||||||
s.c = NewServiceMapController()
|
s.c = NewServiceMapController()
|
||||||
s.c.service.SetConfig(&shared.MizuAgentConfig{
|
s.c.service.Enable()
|
||||||
ServiceMap: true,
|
|
||||||
})
|
|
||||||
s.c.service.NewTCPEntry(TCPEntryA, TCPEntryB, ProtocolHttp)
|
s.c.service.NewTCPEntry(TCPEntryA, TCPEntryB, ProtocolHttp)
|
||||||
|
|
||||||
s.w = httptest.NewRecorder()
|
s.w = httptest.NewRecorder()
|
||||||
@@ -102,13 +99,13 @@ func (s *ServiceMapControllerSuite) TestGet() {
|
|||||||
// response nodes
|
// response nodes
|
||||||
aNode := servicemap.ServiceMapNode{
|
aNode := servicemap.ServiceMapNode{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
Name: TCPEntryA.IP,
|
Name: TCPEntryA.Name,
|
||||||
Entry: TCPEntryA,
|
Entry: TCPEntryA,
|
||||||
Count: 1,
|
Count: 1,
|
||||||
}
|
}
|
||||||
bNode := servicemap.ServiceMapNode{
|
bNode := servicemap.ServiceMapNode{
|
||||||
Id: 2,
|
Id: 2,
|
||||||
Name: TCPEntryB.IP,
|
Name: TCPEntryB.Name,
|
||||||
Entry: TCPEntryB,
|
Entry: TCPEntryB,
|
||||||
Count: 1,
|
Count: 1,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/elastic/go-elasticsearch/v7"
|
"github.com/elastic/go-elasticsearch/v7"
|
||||||
"github.com/up9inc/mizu/shared"
|
"github.com/up9inc/mizu/shared"
|
||||||
"github.com/up9inc/mizu/shared/logger"
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
"github.com/up9inc/mizu/tap/api"
|
"github.com/up9inc/mizu/tap/api"
|
||||||
"net/http"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type client struct {
|
type client struct {
|
||||||
@@ -31,6 +32,9 @@ func GetInstance() *client {
|
|||||||
|
|
||||||
func (client *client) Configure(config shared.ElasticConfig) {
|
func (client *client) Configure(config shared.ElasticConfig) {
|
||||||
if config.Url == "" || config.User == "" || config.Password == "" {
|
if config.Url == "" || config.User == "" || config.Password == "" {
|
||||||
|
if client.es != nil {
|
||||||
|
client.es = nil
|
||||||
|
}
|
||||||
logger.Log.Infof("No elastic configuration was supplied, elastic exporter disabled")
|
logger.Log.Infof("No elastic configuration was supplied, elastic exporter disabled")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -46,13 +50,13 @@ func (client *client) Configure(config shared.ElasticConfig) {
|
|||||||
|
|
||||||
es, err := elasticsearch.NewClient(cfg)
|
es, err := elasticsearch.NewClient(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log.Fatalf("Failed to initialize elastic client %v", err)
|
logger.Log.Errorf("Failed to initialize elastic client %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Have the client instance return a response
|
// Have the client instance return a response
|
||||||
res, err := es.Info()
|
res, err := es.Info()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log.Fatalf("Elastic client.Info() ERROR: %v", err)
|
logger.Log.Errorf("Elastic client.Info() ERROR: %v", err)
|
||||||
} else {
|
} else {
|
||||||
client.es = es
|
client.es = es
|
||||||
client.index = "mizu_traffic_http_" + time.Now().Format("2006_01_02_15_04")
|
client.index = "mizu_traffic_http_" + time.Now().Format("2006_01_02_15_04")
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ func CORSMiddleware() gin.HandlerFunc {
|
|||||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With, x-session-token")
|
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With, x-session-token")
|
||||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
|
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
|
||||||
|
|
||||||
if c.Request.Method == "OPTIONS" {
|
if c.Request.Method == "OPTIONS" {
|
||||||
c.AbortWithStatus(204)
|
c.AbortWithStatus(204)
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ type WebSocketEntryMessage struct {
|
|||||||
Data *tapApi.BaseEntry `json:"data,omitempty"`
|
Data *tapApi.BaseEntry `json:"data,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WebSocketFullEntryMessage struct {
|
||||||
|
*shared.WebSocketMessageMetadata
|
||||||
|
Data *tapApi.Entry `json:"data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type WebSocketTappedEntryMessage struct {
|
type WebSocketTappedEntryMessage struct {
|
||||||
*shared.WebSocketMessageMetadata
|
*shared.WebSocketMessageMetadata
|
||||||
Data *tapApi.OutputChannelItem
|
Data *tapApi.OutputChannelItem
|
||||||
@@ -88,6 +93,16 @@ func CreateBaseEntryWebSocketMessage(base *tapApi.BaseEntry) ([]byte, error) {
|
|||||||
return json.Marshal(message)
|
return json.Marshal(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateFullEntryWebSocketMessage(entry *tapApi.Entry) ([]byte, error) {
|
||||||
|
message := &WebSocketFullEntryMessage{
|
||||||
|
WebSocketMessageMetadata: &shared.WebSocketMessageMetadata{
|
||||||
|
MessageType: shared.WebSocketMessageTypeFullEntry,
|
||||||
|
},
|
||||||
|
Data: entry,
|
||||||
|
}
|
||||||
|
return json.Marshal(message)
|
||||||
|
}
|
||||||
|
|
||||||
func CreateWebsocketTappedEntryMessage(base *tapApi.OutputChannelItem) ([]byte, error) {
|
func CreateWebsocketTappedEntryMessage(base *tapApi.OutputChannelItem) ([]byte, error) {
|
||||||
message := &WebSocketTappedEntryMessage{
|
message := &WebSocketTappedEntryMessage{
|
||||||
WebSocketMessageMetadata: &shared.WebSocketMessageMetadata{
|
WebSocketMessageMetadata: &shared.WebSocketMessageMetadata{
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -139,7 +140,12 @@ func feedEntry(entry *har.Entry, source string, isSync bool, file string) {
|
|||||||
logger.Log.Debugf("Interesting: %s", entry.Request.URL)
|
logger.Log.Debugf("Interesting: %s", entry.Request.URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
ews := EntryWithSource{Entry: *entry, Source: source, Id: uint(0)}
|
u, err := url.Parse(entry.Request.URL)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Errorf("Failed to parse entry URL: %v, err: %v", entry.Request.URL, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ews := EntryWithSource{Entry: *entry, Source: source, Destination: u.Host, Id: uint(0)}
|
||||||
if isSync {
|
if isSync {
|
||||||
GetOasGeneratorInstance().entriesChan <- ews // blocking variant, right?
|
GetOasGeneratorInstance().entriesChan <- ews // blocking variant, right?
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -33,10 +33,23 @@ func (g *oasGenerator) Start() {
|
|||||||
g.entriesChan = make(chan EntryWithSource, 100) // buffer up to 100 entries for OAS processing
|
g.entriesChan = make(chan EntryWithSource, 100) // buffer up to 100 entries for OAS processing
|
||||||
g.ServiceSpecs = &sync.Map{}
|
g.ServiceSpecs = &sync.Map{}
|
||||||
g.started = true
|
g.started = true
|
||||||
go instance.runGeneretor()
|
go instance.runGenerator()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *oasGenerator) runGeneretor() {
|
func (g *oasGenerator) Stop() {
|
||||||
|
if !g.started {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.cancel()
|
||||||
|
g.Reset()
|
||||||
|
g.started = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *oasGenerator) IsStarted() bool {
|
||||||
|
return g.started
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *oasGenerator) runGenerator() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-g.ctx.Done():
|
case <-g.ctx.Done():
|
||||||
@@ -54,11 +67,11 @@ func (g *oasGenerator) runGeneretor() {
|
|||||||
logger.Log.Errorf("Failed to parse entry URL: %v, err: %v", entry.Request.URL, err)
|
logger.Log.Errorf("Failed to parse entry URL: %v, err: %v", entry.Request.URL, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
val, found := g.ServiceSpecs.Load(u.Host)
|
val, found := g.ServiceSpecs.Load(entryWithSource.Destination)
|
||||||
var gen *SpecGen
|
var gen *SpecGen
|
||||||
if !found {
|
if !found {
|
||||||
gen = NewGen(u.Scheme + "://" + u.Host)
|
gen = NewGen(u.Scheme + "://" + entryWithSource.Destination)
|
||||||
g.ServiceSpecs.Store(u.Host, gen)
|
g.ServiceSpecs.Store(entryWithSource.Destination, gen)
|
||||||
} else {
|
} else {
|
||||||
gen = val.(*SpecGen)
|
gen = val.(*SpecGen)
|
||||||
}
|
}
|
||||||
@@ -105,9 +118,10 @@ func newOasGenerator() *oasGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type EntryWithSource struct {
|
type EntryWithSource struct {
|
||||||
Source string
|
Source string
|
||||||
Entry har.Entry
|
Destination string
|
||||||
Id uint
|
Entry har.Entry
|
||||||
|
Id uint
|
||||||
}
|
}
|
||||||
|
|
||||||
type oasGenerator struct {
|
type oasGenerator struct {
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ func (g *SpecGen) handlePathObj(entryWithSource *EntryWithSource) (string, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
if entry.Request.Method == "OPTIONS" {
|
if entry.Request.Method == "OPTIONS" {
|
||||||
logger.Log.Debugf("Dropped traffic entry due to its method: %s", urlParsed.Path)
|
logger.Log.Debugf("Dropped traffic entry due to its method: %s %s", entry.Request.Method, urlParsed.Path)
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,14 +41,31 @@ func outputSpec(label string, spec *openapi.OpenAPI, t *testing.T) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEntries(t *testing.T) {
|
func TestEntries(t *testing.T) {
|
||||||
logger.InitLoggerStderrOnly(logging.INFO)
|
logger.InitLoggerStd(logging.INFO)
|
||||||
files, err := getFiles("./test_artifacts/")
|
files, err := getFiles("./test_artifacts/")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
GetOasGeneratorInstance().Start()
|
GetOasGeneratorInstance().Start()
|
||||||
loadStartingOAS()
|
loadStartingOAS("test_artifacts/catalogue.json", "catalogue")
|
||||||
|
loadStartingOAS("test_artifacts/trcc.json", "trcc-api-service")
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
GetOasGeneratorInstance().ServiceSpecs.Range(func(key, val interface{}) bool {
|
||||||
|
svc := key.(string)
|
||||||
|
t.Logf("Getting spec for %s", svc)
|
||||||
|
gen := val.(*SpecGen)
|
||||||
|
_, err := gen.GetSpec()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
cnt, err := feedEntries(files, true)
|
cnt, err := feedEntries(files, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -154,6 +171,13 @@ func TestFileSingle(t *testing.T) {
|
|||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if os.Getenv("MIZU_OAS_WRITE_FILES") != "" {
|
||||||
|
err = ioutil.WriteFile(file+".spec.json", []byte(specText), 0644)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(diff) > 0 {
|
if len(diff) > 0 {
|
||||||
t.Errorf("Generated spec does not match expected:\n%s", diff.String())
|
t.Errorf("Generated spec does not match expected:\n%s", diff.String())
|
||||||
}
|
}
|
||||||
@@ -175,8 +199,7 @@ func waitQueueProcessed() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadStartingOAS() {
|
func loadStartingOAS(file string, label string) {
|
||||||
file := "test_artifacts/catalogue.json"
|
|
||||||
fd, err := os.Open(file)
|
fd, err := os.Open(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -195,10 +218,10 @@ func loadStartingOAS() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gen := NewGen("catalogue")
|
gen := NewGen(label)
|
||||||
gen.StartFromSpec(doc)
|
gen.StartFromSpec(doc)
|
||||||
|
|
||||||
GetOasGeneratorInstance().ServiceSpecs.Store("catalogue", gen)
|
GetOasGeneratorInstance().ServiceSpecs.Store(label, gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEntriesNegative(t *testing.T) {
|
func TestEntriesNegative(t *testing.T) {
|
||||||
|
|||||||
@@ -538,6 +538,252 @@
|
|||||||
"wait": -1,
|
"wait": -1,
|
||||||
"receive": 1
|
"receive": 1
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:16:22.000000+00:00",
|
||||||
|
"time": 1,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "https://httpbin.org/param-patterns/prefix-gibberish-fine/234324",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"queryString": [],
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": -1,
|
||||||
|
"postData": {
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"content": {
|
||||||
|
"size": 0,
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": 0
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": -1,
|
||||||
|
"wait": -1,
|
||||||
|
"receive": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:16:22.000001+00:00",
|
||||||
|
"time": 1,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "https://httpbin.org/param-patterns/prefix-gibberish-sfdlasdfkadf87sd93284q24r/1",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"queryString": [],
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": -1,
|
||||||
|
"postData": {
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"content": {
|
||||||
|
"size": 0,
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": 0
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": -1,
|
||||||
|
"wait": -1,
|
||||||
|
"receive": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:16:22.000002+00:00",
|
||||||
|
"time": 1,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "https://httpbin.org/param-patterns/prefix-gibberish-adslkfasdf89sa7dfasddafa8a98sd7kansdf/static",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"queryString": [],
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": -1,
|
||||||
|
"postData": {
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"content": {
|
||||||
|
"size": 0,
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": 0
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": -1,
|
||||||
|
"wait": -1,
|
||||||
|
"receive": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:16:22.000003+00:00",
|
||||||
|
"time": 1,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "https://httpbin.org/param-patterns/prefix-gibberish-4jk5l2345h2452l4352435jlk45",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"queryString": [],
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": -1,
|
||||||
|
"postData": {
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"content": {
|
||||||
|
"size": 0,
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": 0
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": -1,
|
||||||
|
"wait": -1,
|
||||||
|
"receive": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:16:22.000004+00:00",
|
||||||
|
"time": 1,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "https://httpbin.org/param-patterns/prefix-gibberish-84395h2j4k35hj243j5h2kl34h54k",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"queryString": [],
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": -1,
|
||||||
|
"postData": {
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"content": {
|
||||||
|
"size": 0,
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": 0
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": -1,
|
||||||
|
"wait": -1,
|
||||||
|
"receive": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:16:22.000002+00:00",
|
||||||
|
"time": 1,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "https://httpbin.org/param-patterns/prefix-gibberish-afterwards/23421",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"queryString": [],
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": -1,
|
||||||
|
"postData": {
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"content": {
|
||||||
|
"size": 0,
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": 0
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": -1,
|
||||||
|
"wait": -1,
|
||||||
|
"receive": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"info": {
|
"info": {
|
||||||
"title": "https://httpbin.org",
|
"title": "https://httpbin.org",
|
||||||
"description": "Mizu observed 13 entries (0 failed), at 0.155 hits/s, average response time is 0.251 seconds",
|
"description": "Mizu observed 19 entries (0 failed), at 0.10 hits/s, average response time is 0.17 seconds",
|
||||||
"version": "1.0"
|
"version": "1.0"
|
||||||
},
|
},
|
||||||
"servers": [
|
"servers": [
|
||||||
@@ -14,8 +14,8 @@
|
|||||||
"/appears-once": {
|
"/appears-once": {
|
||||||
"get": {
|
"get": {
|
||||||
"summary": "/appears-once",
|
"summary": "/appears-once",
|
||||||
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.630 seconds",
|
"description": "Mizu observed 1 entries (0 failed), at 0.00 hits/s, average response time is 0.63 seconds",
|
||||||
"operationId": "89aa39f6-78d0-411b-b701-a33bd77868b0",
|
"operationId": "<UUID4>",
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Successful call with status 200",
|
"description": "Successful call with status 200",
|
||||||
@@ -26,33 +26,33 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"x-last-seen-ts": 1567750580.0471218,
|
|
||||||
"x-counters-total": {
|
|
||||||
"entries": 1,
|
|
||||||
"failures": 0,
|
|
||||||
"firstSeen": 1567750580.0471218,
|
|
||||||
"lastSeen": 1567750580.0471218,
|
|
||||||
"sumRT": 0.63,
|
|
||||||
"sumDuration": 0
|
|
||||||
},
|
|
||||||
"x-counters-per-source": {
|
"x-counters-per-source": {
|
||||||
"": {
|
"": {
|
||||||
"entries": 1,
|
"entries": 1,
|
||||||
"failures": 0,
|
"failures": 0,
|
||||||
"firstSeen": 1567750580.0471218,
|
"firstSeen": 1567750580.04,
|
||||||
"lastSeen": 1567750580.0471218,
|
"lastSeen": 1567750580.04,
|
||||||
"sumRT": 0.63,
|
"sumRT": 0.63,
|
||||||
"sumDuration": 0
|
"sumDuration": 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750580.04,
|
||||||
|
"lastSeen": 1567750580.04,
|
||||||
|
"sumRT": 0.63,
|
||||||
|
"sumDuration": 0
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750580.04,
|
||||||
"x-sample-entry": 0
|
"x-sample-entry": 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/appears-twice": {
|
"/appears-twice": {
|
||||||
"get": {
|
"get": {
|
||||||
"summary": "/appears-twice",
|
"summary": "/appears-twice",
|
||||||
"description": "Mizu observed 2 entries (0 failed), at 0.500 hits/s, average response time is 0.630 seconds",
|
"description": "Mizu observed 2 entries (0 failed), at 0.50 hits/s, average response time is 0.63 seconds",
|
||||||
"operationId": "f5e2b5a2-e01a-45f4-bde1-15a7e7a06d3c",
|
"operationId": "<UUID4>",
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Successful call with status 200",
|
"description": "Successful call with status 200",
|
||||||
@@ -63,33 +63,33 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"x-counters-total": {
|
|
||||||
"entries": 2,
|
|
||||||
"failures": 0,
|
|
||||||
"firstSeen": 1567750580.7471218,
|
|
||||||
"lastSeen": 1567750581.7471218,
|
|
||||||
"sumRT": 1.26,
|
|
||||||
"sumDuration": 1
|
|
||||||
},
|
|
||||||
"x-counters-per-source": {
|
"x-counters-per-source": {
|
||||||
"": {
|
"": {
|
||||||
"entries": 2,
|
"entries": 2,
|
||||||
"failures": 0,
|
"failures": 0,
|
||||||
"firstSeen": 1567750580.7471218,
|
"firstSeen": 1567750580.74,
|
||||||
"lastSeen": 1567750581.7471218,
|
"lastSeen": 1567750581.74,
|
||||||
"sumRT": 1.26,
|
"sumRT": 1.26,
|
||||||
"sumDuration": 1
|
"sumDuration": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"x-sample-entry": 0,
|
"x-counters-total": {
|
||||||
"x-last-seen-ts": 1567750581.7471218
|
"entries": 2,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750580.74,
|
||||||
|
"lastSeen": 1567750581.74,
|
||||||
|
"sumRT": 1.26,
|
||||||
|
"sumDuration": 1
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750581.74,
|
||||||
|
"x-sample-entry": 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/body-optional": {
|
"/body-optional": {
|
||||||
"post": {
|
"post": {
|
||||||
"summary": "/body-optional",
|
"summary": "/body-optional",
|
||||||
"description": "Mizu observed 3 entries (0 failed), at 0.003 hits/s, average response time is 0.001 seconds",
|
"description": "Mizu observed 3 entries (0 failed), at 0.00 hits/s, average response time is 0.00 seconds",
|
||||||
"operationId": "14d5b1c2-dc03-4ee5-baaa-5c7992acc82e",
|
"operationId": "<UUID4>",
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Successful call with status 200",
|
"description": "Successful call with status 200",
|
||||||
@@ -98,26 +98,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"x-counters-total": {
|
|
||||||
"entries": 3,
|
|
||||||
"failures": 0,
|
|
||||||
"firstSeen": 1567750581.7471218,
|
|
||||||
"lastSeen": 1567750581.757122,
|
|
||||||
"sumRT": 0.003,
|
|
||||||
"sumDuration": 0.010000228881835938
|
|
||||||
},
|
|
||||||
"x-sample-entry": 0,
|
|
||||||
"x-counters-per-source": {
|
"x-counters-per-source": {
|
||||||
"": {
|
"": {
|
||||||
"entries": 3,
|
"entries": 3,
|
||||||
"failures": 0,
|
"failures": 0,
|
||||||
"firstSeen": 1567750581.7471218,
|
"firstSeen": 1567750581.74,
|
||||||
"lastSeen": 1567750581.757122,
|
"lastSeen": 1567750581.75,
|
||||||
"sumRT": 0.003,
|
"sumRT": 0.00,
|
||||||
"sumDuration": 0.010000228881835938
|
"sumDuration": 0.01
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"x-last-seen-ts": 1567750581.757122,
|
"x-counters-total": {
|
||||||
|
"entries": 3,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750581.74,
|
||||||
|
"lastSeen": 1567750581.75,
|
||||||
|
"sumRT": 0.00,
|
||||||
|
"sumDuration": 0.01
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750581.75,
|
||||||
|
"x-sample-entry": 0,
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
"description": "Generic request body",
|
"description": "Generic request body",
|
||||||
"content": {
|
"content": {
|
||||||
@@ -131,8 +131,8 @@
|
|||||||
"/body-required": {
|
"/body-required": {
|
||||||
"post": {
|
"post": {
|
||||||
"summary": "/body-required",
|
"summary": "/body-required",
|
||||||
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.001 seconds",
|
"description": "Mizu observed 1 entries (0 failed), at 0.00 hits/s, average response time is 0.00 seconds",
|
||||||
"operationId": "d0958c5a-dce6-4616-99f4-201dbc51457a",
|
"operationId": "<UUID4>",
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Successful call with status 200",
|
"description": "Successful call with status 200",
|
||||||
@@ -145,22 +145,22 @@
|
|||||||
"": {
|
"": {
|
||||||
"entries": 1,
|
"entries": 1,
|
||||||
"failures": 0,
|
"failures": 0,
|
||||||
"firstSeen": 1567750581.757122,
|
"firstSeen": 1567750581.75,
|
||||||
"lastSeen": 1567750581.757122,
|
"lastSeen": 1567750581.75,
|
||||||
"sumRT": 0.001,
|
"sumRT": 0.00,
|
||||||
"sumDuration": 0
|
"sumDuration": 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"x-sample-entry": 0,
|
|
||||||
"x-counters-total": {
|
"x-counters-total": {
|
||||||
"entries": 1,
|
"entries": 1,
|
||||||
"failures": 0,
|
"failures": 0,
|
||||||
"firstSeen": 1567750581.757122,
|
"firstSeen": 1567750581.75,
|
||||||
"lastSeen": 1567750581.757122,
|
"lastSeen": 1567750581.75,
|
||||||
"sumRT": 0.001,
|
"sumRT": 0.00,
|
||||||
"sumDuration": 0
|
"sumDuration": 0
|
||||||
},
|
},
|
||||||
"x-last-seen-ts": 1567750581.757122,
|
"x-last-seen-ts": 1567750581.75,
|
||||||
|
"x-sample-entry": 0,
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
"description": "Generic request body",
|
"description": "Generic request body",
|
||||||
"content": {
|
"content": {
|
||||||
@@ -175,8 +175,8 @@
|
|||||||
"/form-multipart": {
|
"/form-multipart": {
|
||||||
"post": {
|
"post": {
|
||||||
"summary": "/form-multipart",
|
"summary": "/form-multipart",
|
||||||
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.001 seconds",
|
"description": "Mizu observed 1 entries (0 failed), at 0.00 hits/s, average response time is 0.00 seconds",
|
||||||
"operationId": "cab5a2f3-c18a-4d5a-8f92-e40da4fd6603",
|
"operationId": "<UUID4>",
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Successful call with status 200",
|
"description": "Successful call with status 200",
|
||||||
@@ -187,26 +187,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"x-sample-entry": 0,
|
|
||||||
"x-counters-per-source": {
|
"x-counters-per-source": {
|
||||||
"": {
|
"": {
|
||||||
"entries": 1,
|
"entries": 1,
|
||||||
"failures": 0,
|
"failures": 0,
|
||||||
"firstSeen": 1567750582.7471218,
|
"firstSeen": 1567750582.74,
|
||||||
"lastSeen": 1567750582.7471218,
|
"lastSeen": 1567750582.74,
|
||||||
"sumRT": 0.001,
|
"sumRT": 0.00,
|
||||||
"sumDuration": 0
|
"sumDuration": 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"x-last-seen-ts": 1567750582.7471218,
|
|
||||||
"x-counters-total": {
|
"x-counters-total": {
|
||||||
"entries": 1,
|
"entries": 1,
|
||||||
"failures": 0,
|
"failures": 0,
|
||||||
"firstSeen": 1567750582.7471218,
|
"firstSeen": 1567750582.74,
|
||||||
"lastSeen": 1567750582.7471218,
|
"lastSeen": 1567750582.74,
|
||||||
"sumRT": 0.001,
|
"sumRT": 0.00,
|
||||||
"sumDuration": 0
|
"sumDuration": 0
|
||||||
},
|
},
|
||||||
|
"x-last-seen-ts": 1567750582.74,
|
||||||
|
"x-sample-entry": 0,
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
"description": "Generic request body",
|
"description": "Generic request body",
|
||||||
"content": {
|
"content": {
|
||||||
@@ -243,8 +243,8 @@
|
|||||||
"/form-urlencoded": {
|
"/form-urlencoded": {
|
||||||
"post": {
|
"post": {
|
||||||
"summary": "/form-urlencoded",
|
"summary": "/form-urlencoded",
|
||||||
"description": "Mizu observed 2 entries (0 failed), at 0.500 hits/s, average response time is 0.001 seconds",
|
"description": "Mizu observed 2 entries (0 failed), at 0.50 hits/s, average response time is 0.00 seconds",
|
||||||
"operationId": "7c373ad7-6ab5-422e-971b-1cf56b18a7a2",
|
"operationId": "<UUID4>",
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Successful call with status 200",
|
"description": "Successful call with status 200",
|
||||||
@@ -253,25 +253,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"x-last-seen-ts": 1567750581.7471218,
|
|
||||||
"x-counters-total": {
|
|
||||||
"entries": 2,
|
|
||||||
"failures": 0,
|
|
||||||
"firstSeen": 1567750580.7471218,
|
|
||||||
"lastSeen": 1567750581.7471218,
|
|
||||||
"sumRT": 0.002,
|
|
||||||
"sumDuration": 1
|
|
||||||
},
|
|
||||||
"x-counters-per-source": {
|
"x-counters-per-source": {
|
||||||
"": {
|
"": {
|
||||||
"entries": 2,
|
"entries": 2,
|
||||||
"failures": 0,
|
"failures": 0,
|
||||||
"firstSeen": 1567750580.7471218,
|
"firstSeen": 1567750580.74,
|
||||||
"lastSeen": 1567750581.7471218,
|
"lastSeen": 1567750581.74,
|
||||||
"sumRT": 0.002,
|
"sumRT": 0.00,
|
||||||
"sumDuration": 1
|
"sumDuration": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 2,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750580.74,
|
||||||
|
"lastSeen": 1567750581.74,
|
||||||
|
"sumRT": 0.00,
|
||||||
|
"sumDuration": 1
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750581.74,
|
||||||
"x-sample-entry": 0,
|
"x-sample-entry": 0,
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
"description": "Generic request body",
|
"description": "Generic request body",
|
||||||
@@ -319,11 +319,347 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/param-patterns/prefix-gibberish-fine/{prefixgibberishfineId}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"param-patterns"
|
||||||
|
],
|
||||||
|
"summary": "/param-patterns/prefix-gibberish-fine/{prefixgibberishfineId}",
|
||||||
|
"description": "Mizu observed 1 entries (0 failed), at 0.00 hits/s, average response time is 0.00 seconds",
|
||||||
|
"operationId": "<UUID4>",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful call with status 200",
|
||||||
|
"content": {
|
||||||
|
"": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-counters-per-source": {
|
||||||
|
"": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750582,
|
||||||
|
"lastSeen": 1567750582,
|
||||||
|
"sumRT": 0.00,
|
||||||
|
"sumDuration": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750582,
|
||||||
|
"lastSeen": 1567750582,
|
||||||
|
"sumRT": 0.00,
|
||||||
|
"sumDuration": 0
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750582,
|
||||||
|
"x-sample-entry": 0
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "prefixgibberishfineId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"style": "simple",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"example #0": {
|
||||||
|
"value": "234324"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"/param-patterns/{parampatternId}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"param-patterns"
|
||||||
|
],
|
||||||
|
"summary": "/param-patterns/{parampatternId}",
|
||||||
|
"description": "Mizu observed 2 entries (0 failed), at 0.00 hits/s, average response time is 0.00 seconds",
|
||||||
|
"operationId": "<UUID4>",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful call with status 200",
|
||||||
|
"content": {
|
||||||
|
"": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-counters-per-source": {
|
||||||
|
"": {
|
||||||
|
"entries": 2,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750582.00,
|
||||||
|
"lastSeen": 1567750582.00,
|
||||||
|
"sumRT": 0.00,
|
||||||
|
"sumDuration": 9.53e-7
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 2,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750582.00,
|
||||||
|
"lastSeen": 1567750582.00,
|
||||||
|
"sumRT": 0.00,
|
||||||
|
"sumDuration": 9.53e-7
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750582.00,
|
||||||
|
"x-sample-entry": 0
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "parampatternId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"style": "simple",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^prefix-gibberish-.+"
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"example #0": {
|
||||||
|
"value": "prefix-gibberish-sfdlasdfkadf87sd93284q24r"
|
||||||
|
},
|
||||||
|
"example #1": {
|
||||||
|
"value": "prefix-gibberish-adslkfasdf89sa7dfasddafa8a98sd7kansdf"
|
||||||
|
},
|
||||||
|
"example #2": {
|
||||||
|
"value": "prefix-gibberish-4jk5l2345h2452l4352435jlk45"
|
||||||
|
},
|
||||||
|
"example #3": {
|
||||||
|
"value": "prefix-gibberish-84395h2j4k35hj243j5h2kl34h54k"
|
||||||
|
},
|
||||||
|
"example #4": {
|
||||||
|
"value": "prefix-gibberish-afterwards"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"/param-patterns/{parampatternId}/1": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"param-patterns"
|
||||||
|
],
|
||||||
|
"summary": "/param-patterns/{parampatternId}/1",
|
||||||
|
"description": "Mizu observed 1 entries (0 failed), at 0.00 hits/s, average response time is 0.00 seconds",
|
||||||
|
"operationId": "<UUID4>",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful call with status 200",
|
||||||
|
"content": {
|
||||||
|
"": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-counters-per-source": {
|
||||||
|
"": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750582.00,
|
||||||
|
"lastSeen": 1567750582.00,
|
||||||
|
"sumRT": 0.00,
|
||||||
|
"sumDuration": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750582.00,
|
||||||
|
"lastSeen": 1567750582.00,
|
||||||
|
"sumRT": 0.00,
|
||||||
|
"sumDuration": 0
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750582.00,
|
||||||
|
"x-sample-entry": 0
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "parampatternId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"style": "simple",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^prefix-gibberish-.+"
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"example #0": {
|
||||||
|
"value": "prefix-gibberish-sfdlasdfkadf87sd93284q24r"
|
||||||
|
},
|
||||||
|
"example #1": {
|
||||||
|
"value": "prefix-gibberish-adslkfasdf89sa7dfasddafa8a98sd7kansdf"
|
||||||
|
},
|
||||||
|
"example #2": {
|
||||||
|
"value": "prefix-gibberish-4jk5l2345h2452l4352435jlk45"
|
||||||
|
},
|
||||||
|
"example #3": {
|
||||||
|
"value": "prefix-gibberish-84395h2j4k35hj243j5h2kl34h54k"
|
||||||
|
},
|
||||||
|
"example #4": {
|
||||||
|
"value": "prefix-gibberish-afterwards"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"/param-patterns/{parampatternId}/static": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"param-patterns"
|
||||||
|
],
|
||||||
|
"summary": "/param-patterns/{parampatternId}/static",
|
||||||
|
"description": "Mizu observed 1 entries (0 failed), at 0.00 hits/s, average response time is 0.00 seconds",
|
||||||
|
"operationId": "<UUID4>",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful call with status 200",
|
||||||
|
"content": {
|
||||||
|
"": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-counters-per-source": {
|
||||||
|
"": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750582.00,
|
||||||
|
"lastSeen": 1567750582.00,
|
||||||
|
"sumRT": 0.00,
|
||||||
|
"sumDuration": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750582.00,
|
||||||
|
"lastSeen": 1567750582.00,
|
||||||
|
"sumRT": 0.00,
|
||||||
|
"sumDuration": 0
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750582.00,
|
||||||
|
"x-sample-entry": 0
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "parampatternId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"style": "simple",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^prefix-gibberish-.+"
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"example #0": {
|
||||||
|
"value": "prefix-gibberish-sfdlasdfkadf87sd93284q24r"
|
||||||
|
},
|
||||||
|
"example #1": {
|
||||||
|
"value": "prefix-gibberish-adslkfasdf89sa7dfasddafa8a98sd7kansdf"
|
||||||
|
},
|
||||||
|
"example #2": {
|
||||||
|
"value": "prefix-gibberish-4jk5l2345h2452l4352435jlk45"
|
||||||
|
},
|
||||||
|
"example #3": {
|
||||||
|
"value": "prefix-gibberish-84395h2j4k35hj243j5h2kl34h54k"
|
||||||
|
},
|
||||||
|
"example #4": {
|
||||||
|
"value": "prefix-gibberish-afterwards"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"/param-patterns/{parampatternId}/{param1}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"param-patterns"
|
||||||
|
],
|
||||||
|
"summary": "/param-patterns/{parampatternId}/{param1}",
|
||||||
|
"description": "Mizu observed 1 entries (0 failed), at 0.00 hits/s, average response time is 0.00 seconds",
|
||||||
|
"operationId": "<UUID4>",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful call with status 200",
|
||||||
|
"content": {
|
||||||
|
"": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-counters-per-source": {
|
||||||
|
"": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750582.00,
|
||||||
|
"lastSeen": 1567750582.00,
|
||||||
|
"sumRT": 0.00,
|
||||||
|
"sumDuration": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750582.00,
|
||||||
|
"lastSeen": 1567750582.00,
|
||||||
|
"sumRT": 0.00,
|
||||||
|
"sumDuration": 0
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750582.00,
|
||||||
|
"x-sample-entry": 0
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "param1",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"style": "simple",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"example #0": {
|
||||||
|
"value": "23421"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "parampatternId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"style": "simple",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^prefix-gibberish-.+"
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"example #0": {
|
||||||
|
"value": "prefix-gibberish-sfdlasdfkadf87sd93284q24r"
|
||||||
|
},
|
||||||
|
"example #1": {
|
||||||
|
"value": "prefix-gibberish-adslkfasdf89sa7dfasddafa8a98sd7kansdf"
|
||||||
|
},
|
||||||
|
"example #2": {
|
||||||
|
"value": "prefix-gibberish-4jk5l2345h2452l4352435jlk45"
|
||||||
|
},
|
||||||
|
"example #3": {
|
||||||
|
"value": "prefix-gibberish-84395h2j4k35hj243j5h2kl34h54k"
|
||||||
|
},
|
||||||
|
"example #4": {
|
||||||
|
"value": "prefix-gibberish-afterwards"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"/{Id}": {
|
"/{Id}": {
|
||||||
"get": {
|
"get": {
|
||||||
"summary": "/{Id}",
|
"summary": "/{Id}",
|
||||||
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.630 seconds",
|
"description": "Mizu observed 1 entries (0 failed), at 0.00 hits/s, average response time is 0.63 seconds",
|
||||||
"operationId": "99f1d11f-29c0-48f9-8bf0-9f4b407c7c3f",
|
"operationId": "<UUID4>",
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Successful call with status 200",
|
"description": "Successful call with status 200",
|
||||||
@@ -334,26 +670,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"x-sample-entry": 0,
|
|
||||||
"x-last-seen-ts": 1567750579.7471218,
|
|
||||||
"x-counters-total": {
|
|
||||||
"entries": 1,
|
|
||||||
"failures": 0,
|
|
||||||
"firstSeen": 1567750579.7471218,
|
|
||||||
"lastSeen": 1567750579.7471218,
|
|
||||||
"sumRT": 0.63,
|
|
||||||
"sumDuration": 0
|
|
||||||
},
|
|
||||||
"x-counters-per-source": {
|
"x-counters-per-source": {
|
||||||
"": {
|
"": {
|
||||||
"entries": 1,
|
"entries": 1,
|
||||||
"failures": 0,
|
"failures": 0,
|
||||||
"firstSeen": 1567750579.7471218,
|
"firstSeen": 1567750579.74,
|
||||||
"lastSeen": 1567750579.7471218,
|
"lastSeen": 1567750579.74,
|
||||||
"sumRT": 0.63,
|
"sumRT": 0.63,
|
||||||
"sumDuration": 0
|
"sumDuration": 0
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750579.74,
|
||||||
|
"lastSeen": 1567750579.74,
|
||||||
|
"sumRT": 0.63,
|
||||||
|
"sumDuration": 0
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750579.74,
|
||||||
|
"x-sample-entry": 0
|
||||||
},
|
},
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
@@ -366,10 +702,10 @@
|
|||||||
},
|
},
|
||||||
"examples": {
|
"examples": {
|
||||||
"example #0": {
|
"example #0": {
|
||||||
"value": "e21f7112-3d3b-4632-9da3-a4af2e0e9166"
|
"value": "<UUID4>"
|
||||||
},
|
},
|
||||||
"example #1": {
|
"example #1": {
|
||||||
"value": "952bea17-3776-11ea-9341-42010a84012a"
|
"value": "<UUID4>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -378,8 +714,8 @@
|
|||||||
"/{Id}/sub1": {
|
"/{Id}/sub1": {
|
||||||
"get": {
|
"get": {
|
||||||
"summary": "/{Id}/sub1",
|
"summary": "/{Id}/sub1",
|
||||||
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.111 seconds",
|
"description": "Mizu observed 1 entries (0 failed), at 0.00 hits/s, average response time is 0.11 seconds",
|
||||||
"operationId": "f7e299d2-253c-4eef-975c-9a5659a7fc50",
|
"operationId": "<UUID4>",
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Successful call with status 200",
|
"description": "Successful call with status 200",
|
||||||
@@ -388,25 +724,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"x-last-seen-ts": 1567750483.864529,
|
|
||||||
"x-counters-total": {
|
|
||||||
"entries": 1,
|
|
||||||
"failures": 0,
|
|
||||||
"firstSeen": 1567750483.864529,
|
|
||||||
"lastSeen": 1567750483.864529,
|
|
||||||
"sumRT": 0.111,
|
|
||||||
"sumDuration": 0
|
|
||||||
},
|
|
||||||
"x-counters-per-source": {
|
"x-counters-per-source": {
|
||||||
"": {
|
"": {
|
||||||
"entries": 1,
|
"entries": 1,
|
||||||
"failures": 0,
|
"failures": 0,
|
||||||
"firstSeen": 1567750483.864529,
|
"firstSeen": 1567750483.86,
|
||||||
"lastSeen": 1567750483.864529,
|
"lastSeen": 1567750483.86,
|
||||||
"sumRT": 0.111,
|
"sumRT": 0.11,
|
||||||
"sumDuration": 0
|
"sumDuration": 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750483.86,
|
||||||
|
"lastSeen": 1567750483.86,
|
||||||
|
"sumRT": 0.11,
|
||||||
|
"sumDuration": 0
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750483.86,
|
||||||
"x-sample-entry": 0
|
"x-sample-entry": 0
|
||||||
},
|
},
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -420,10 +756,10 @@
|
|||||||
},
|
},
|
||||||
"examples": {
|
"examples": {
|
||||||
"example #0": {
|
"example #0": {
|
||||||
"value": "e21f7112-3d3b-4632-9da3-a4af2e0e9166"
|
"value": "<UUID4>"
|
||||||
},
|
},
|
||||||
"example #1": {
|
"example #1": {
|
||||||
"value": "952bea17-3776-11ea-9341-42010a84012a"
|
"value": "<UUID4>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -432,8 +768,8 @@
|
|||||||
"/{Id}/sub2": {
|
"/{Id}/sub2": {
|
||||||
"get": {
|
"get": {
|
||||||
"summary": "/{Id}/sub2",
|
"summary": "/{Id}/sub2",
|
||||||
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.630 seconds",
|
"description": "Mizu observed 1 entries (0 failed), at 0.00 hits/s, average response time is 0.63 seconds",
|
||||||
"operationId": "23a54e06-4298-4ea5-b1f0-09b0354a0598",
|
"operationId": "<UUID4>",
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Successful call with status 200",
|
"description": "Successful call with status 200",
|
||||||
@@ -444,25 +780,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"x-last-seen-ts": 1567750578.7471218,
|
|
||||||
"x-counters-total": {
|
|
||||||
"entries": 1,
|
|
||||||
"failures": 0,
|
|
||||||
"firstSeen": 1567750578.7471218,
|
|
||||||
"lastSeen": 1567750578.7471218,
|
|
||||||
"sumRT": 0.63,
|
|
||||||
"sumDuration": 0
|
|
||||||
},
|
|
||||||
"x-counters-per-source": {
|
"x-counters-per-source": {
|
||||||
"": {
|
"": {
|
||||||
"entries": 1,
|
"entries": 1,
|
||||||
"failures": 0,
|
"failures": 0,
|
||||||
"firstSeen": 1567750578.7471218,
|
"firstSeen": 1567750578.74,
|
||||||
"lastSeen": 1567750578.7471218,
|
"lastSeen": 1567750578.74,
|
||||||
"sumRT": 0.63,
|
"sumRT": 0.63,
|
||||||
"sumDuration": 0
|
"sumDuration": 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750578.74,
|
||||||
|
"lastSeen": 1567750578.74,
|
||||||
|
"sumRT": 0.63,
|
||||||
|
"sumDuration": 0
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750578.74,
|
||||||
"x-sample-entry": 0
|
"x-sample-entry": 0
|
||||||
},
|
},
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -476,32 +812,32 @@
|
|||||||
},
|
},
|
||||||
"examples": {
|
"examples": {
|
||||||
"example #0": {
|
"example #0": {
|
||||||
"value": "e21f7112-3d3b-4632-9da3-a4af2e0e9166"
|
"value": "<UUID4>"
|
||||||
},
|
},
|
||||||
"example #1": {
|
"example #1": {
|
||||||
"value": "952bea17-3776-11ea-9341-42010a84012a"
|
"value": "<UUID4>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"x-counters-total": {
|
|
||||||
"entries": 13,
|
|
||||||
"failures": 0,
|
|
||||||
"firstSeen": 1567750483.864529,
|
|
||||||
"lastSeen": 1567750582.7471218,
|
|
||||||
"sumRT": 3.268,
|
|
||||||
"sumDuration": 2.010000228881836
|
|
||||||
},
|
|
||||||
"x-counters-per-source": {
|
"x-counters-per-source": {
|
||||||
"": {
|
"": {
|
||||||
"entries": 13,
|
"entries": 19,
|
||||||
"failures": 0,
|
"failures": 0,
|
||||||
"firstSeen": 1567750483.864529,
|
"firstSeen": 1567750483.86,
|
||||||
"lastSeen": 1567750582.7471218,
|
"lastSeen": 1567750582.74,
|
||||||
"sumRT": 3.268,
|
"sumRT": 3.27,
|
||||||
"sumDuration": 2.010000228881836
|
"sumDuration": 2.01
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 19,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750483.86,
|
||||||
|
"lastSeen": 1567750582.74,
|
||||||
|
"sumRT": 3.27,
|
||||||
|
"sumDuration": 2.01
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
50
agent/pkg/oas/test_artifacts/trcc.json
Normal file
50
agent/pkg/oas/test_artifacts/trcc.json
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"info": {
|
||||||
|
"title": "Preloaded TRCC",
|
||||||
|
"version": "0.1",
|
||||||
|
"description": "Test file for loading pre-existing OAS"
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"/models/{id}": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"style": "simple",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": ".+(_|-|\\.).+"
|
||||||
|
},
|
||||||
|
"example": "some-uuid-maybe"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"/models/{id}/{id2}": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"style": "simple",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": ".+(_|-|\\.).+"
|
||||||
|
},
|
||||||
|
"example": "some-uuid-maybe"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id2",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"style": "simple",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "\\d+"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
package oas
|
package oas
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"encoding/json"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/chanced/openapi"
|
"github.com/chanced/openapi"
|
||||||
"github.com/up9inc/mizu/shared/logger"
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NodePath = []string
|
type NodePath = []string
|
||||||
@@ -49,8 +50,8 @@ func (n *Node) getOrSet(path NodePath, existingPathObj *openapi.PathObj) (node *
|
|||||||
node = n.searchInConstants(pathChunk)
|
node = n.searchInConstants(pathChunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
if node == nil {
|
if node == nil && pathChunk != "" {
|
||||||
node = n.searchInParams(paramObj, chunkIsGibberish)
|
node = n.searchInParams(paramObj, pathChunk, chunkIsGibberish)
|
||||||
}
|
}
|
||||||
|
|
||||||
// still no node found, should create it
|
// still no node found, should create it
|
||||||
@@ -76,6 +77,10 @@ func (n *Node) getOrSet(path NodePath, existingPathObj *openapi.PathObj) (node *
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log.Warningf("Failed to add example to a parameter: %s", err)
|
logger.Log.Warningf("Failed to add example to a parameter: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(*exmp) >= 3 && node.pathParam.Schema.Pattern == nil { // is it enough to decide on 2 samples?
|
||||||
|
node.pathParam.Schema.Pattern = getPatternFromExamples(exmp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: eat up trailing slash, in a smart way: node.pathObj!=nil && path[1]==""
|
// TODO: eat up trailing slash, in a smart way: node.pathObj!=nil && path[1]==""
|
||||||
@@ -88,6 +93,57 @@ func (n *Node) getOrSet(path NodePath, existingPathObj *openapi.PathObj) (node *
|
|||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getPatternFromExamples(exmp *openapi.Examples) *openapi.Regexp {
|
||||||
|
allInts := true
|
||||||
|
strs := make([]string, 0)
|
||||||
|
for _, example := range *exmp {
|
||||||
|
exampleObj, err := example.ResolveExample(exampleResolver)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var value string
|
||||||
|
err = json.Unmarshal(exampleObj.Value, &value)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Warningf("Failed decoding parameter example into string: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
strs = append(strs, value)
|
||||||
|
|
||||||
|
if _, err := strconv.Atoi(value); err != nil {
|
||||||
|
allInts = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if allInts {
|
||||||
|
re := new(openapi.Regexp)
|
||||||
|
re.Regexp = regexp.MustCompile(`\d+`)
|
||||||
|
return re
|
||||||
|
} else {
|
||||||
|
prefix := longestCommonXfixStr(strs, true)
|
||||||
|
suffix := longestCommonXfixStr(strs, false)
|
||||||
|
|
||||||
|
pat := ""
|
||||||
|
separators := "-._/:|*,+" // TODO: we could also cut prefix till the last separator
|
||||||
|
if len(prefix) > 0 && strings.Contains(separators, string(prefix[len(prefix)-1])) {
|
||||||
|
pat = "^" + regexp.QuoteMeta(prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
pat += ".+"
|
||||||
|
|
||||||
|
if len(suffix) > 0 && strings.Contains(separators, string(suffix[0])) {
|
||||||
|
pat += regexp.QuoteMeta(suffix) + "$"
|
||||||
|
}
|
||||||
|
|
||||||
|
if pat != ".+" {
|
||||||
|
re := new(openapi.Regexp)
|
||||||
|
re.Regexp = regexp.MustCompile(pat)
|
||||||
|
return re
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (n *Node) createParam() *openapi.ParameterObj {
|
func (n *Node) createParam() *openapi.ParameterObj {
|
||||||
name := "param"
|
name := "param"
|
||||||
|
|
||||||
@@ -118,23 +174,30 @@ func (n *Node) createParam() *openapi.ParameterObj {
|
|||||||
return newParam
|
return newParam
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) searchInParams(paramObj *openapi.ParameterObj, chunkIsGibberish bool) *Node {
|
func (n *Node) searchInParams(paramObj *openapi.ParameterObj, chunk string, chunkIsGibberish bool) *Node {
|
||||||
// look among params
|
// look among params
|
||||||
if paramObj != nil || chunkIsGibberish {
|
for _, subnode := range n.children {
|
||||||
for _, subnode := range n.children {
|
if subnode.constant != nil {
|
||||||
if subnode.constant != nil {
|
continue
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: check the regex pattern of param? for exceptions etc
|
|
||||||
|
|
||||||
if paramObj != nil {
|
|
||||||
// TODO: mergeParam(subnode.pathParam, paramObj)
|
|
||||||
return subnode
|
|
||||||
} else {
|
|
||||||
return subnode
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if paramObj != nil {
|
||||||
|
// TODO: mergeParam(subnode.pathParam, paramObj)
|
||||||
|
return subnode
|
||||||
|
} else if subnode.pathParam.Schema.Pattern != nil { // it has defined param pattern, have to respect it
|
||||||
|
// TODO: and not in exceptions
|
||||||
|
if subnode.pathParam.Schema.Pattern.Match([]byte(chunk)) {
|
||||||
|
return subnode
|
||||||
|
} else if chunkIsGibberish {
|
||||||
|
// TODO: what to do if gibberish chunk does not match the pattern and not in exceptions?
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else if chunkIsGibberish {
|
||||||
|
return subnode
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -290,6 +290,53 @@ func longestCommonXfix(strs [][]string, pre bool) []string { // https://github.c
|
|||||||
return xfix
|
return xfix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func longestCommonXfixStr(strs []string, pre bool) string { // https://github.com/jpillora/longestcommon
|
||||||
|
//short-circuit empty list
|
||||||
|
if len(strs) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
xfix := strs[0]
|
||||||
|
//short-circuit single-element list
|
||||||
|
if len(strs) == 1 {
|
||||||
|
return xfix
|
||||||
|
}
|
||||||
|
//compare first to rest
|
||||||
|
for _, str := range strs[1:] {
|
||||||
|
xfixl := len(xfix)
|
||||||
|
strl := len(str)
|
||||||
|
//short-circuit empty strings
|
||||||
|
if xfixl == 0 || strl == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
//maximum possible length
|
||||||
|
maxl := xfixl
|
||||||
|
if strl < maxl {
|
||||||
|
maxl = strl
|
||||||
|
}
|
||||||
|
//compare letters
|
||||||
|
if pre {
|
||||||
|
//prefix, iterate left to right
|
||||||
|
for i := 0; i < maxl; i++ {
|
||||||
|
if xfix[i] != str[i] {
|
||||||
|
xfix = xfix[:i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//suffix, iternate right to left
|
||||||
|
for i := 0; i < maxl; i++ {
|
||||||
|
xi := xfixl - i - 1
|
||||||
|
si := strl - i - 1
|
||||||
|
if xfix[xi] != str[si] {
|
||||||
|
xfix = xfix[xi+1:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return xfix
|
||||||
|
}
|
||||||
|
|
||||||
func getSimilarPrefix(strs []string) string {
|
func getSimilarPrefix(strs []string) string {
|
||||||
chunked := make([][]string, 0)
|
chunked := make([][]string, 0)
|
||||||
for _, item := range strs {
|
for _, item := range strs {
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ type Resolver struct {
|
|||||||
namespace string
|
namespace string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ResolvedObjectInfo struct {
|
||||||
|
FullAddress string
|
||||||
|
Namespace string
|
||||||
|
}
|
||||||
|
|
||||||
func (resolver *Resolver) Start(ctx context.Context) {
|
func (resolver *Resolver) Start(ctx context.Context) {
|
||||||
if !resolver.isStarted {
|
if !resolver.isStarted {
|
||||||
resolver.isStarted = true
|
resolver.isStarted = true
|
||||||
@@ -40,12 +45,12 @@ func (resolver *Resolver) Start(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (resolver *Resolver) Resolve(name string) string {
|
func (resolver *Resolver) Resolve(name string) *ResolvedObjectInfo {
|
||||||
resolvedName, isFound := resolver.nameMap.Get(name)
|
resolvedName, isFound := resolver.nameMap.Get(name)
|
||||||
if !isFound {
|
if !isFound {
|
||||||
return ""
|
return nil
|
||||||
}
|
}
|
||||||
return resolvedName.(string)
|
return resolvedName.(*ResolvedObjectInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (resolver *Resolver) GetMap() cmap.ConcurrentMap {
|
func (resolver *Resolver) GetMap() cmap.ConcurrentMap {
|
||||||
@@ -71,7 +76,7 @@ func (resolver *Resolver) watchPods(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
if event.Type == watch.Deleted {
|
if event.Type == watch.Deleted {
|
||||||
pod := event.Object.(*corev1.Pod)
|
pod := event.Object.(*corev1.Pod)
|
||||||
resolver.saveResolvedName(pod.Status.PodIP, "", event.Type)
|
resolver.saveResolvedName(pod.Status.PodIP, "", pod.Namespace, event.Type)
|
||||||
}
|
}
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
watcher.Stop()
|
watcher.Stop()
|
||||||
@@ -106,10 +111,10 @@ func (resolver *Resolver) watchEndpoints(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
if subset.Addresses != nil {
|
if subset.Addresses != nil {
|
||||||
for _, address := range subset.Addresses {
|
for _, address := range subset.Addresses {
|
||||||
resolver.saveResolvedName(address.IP, serviceHostname, event.Type)
|
resolver.saveResolvedName(address.IP, serviceHostname, endpoint.Namespace, event.Type)
|
||||||
for _, port := range ports {
|
for _, port := range ports {
|
||||||
ipWithPort := fmt.Sprintf("%s:%d", address.IP, port)
|
ipWithPort := fmt.Sprintf("%s:%d", address.IP, port)
|
||||||
resolver.saveResolvedName(ipWithPort, serviceHostname, event.Type)
|
resolver.saveResolvedName(ipWithPort, serviceHostname, endpoint.Namespace, event.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,19 +144,19 @@ func (resolver *Resolver) watchServices(ctx context.Context) error {
|
|||||||
service := event.Object.(*corev1.Service)
|
service := event.Object.(*corev1.Service)
|
||||||
serviceHostname := fmt.Sprintf("%s.%s", service.Name, service.Namespace)
|
serviceHostname := fmt.Sprintf("%s.%s", service.Name, service.Namespace)
|
||||||
if service.Spec.ClusterIP != "" && service.Spec.ClusterIP != kubClientNullString {
|
if service.Spec.ClusterIP != "" && service.Spec.ClusterIP != kubClientNullString {
|
||||||
resolver.saveResolvedName(service.Spec.ClusterIP, serviceHostname, event.Type)
|
resolver.saveResolvedName(service.Spec.ClusterIP, serviceHostname, service.Namespace, event.Type)
|
||||||
if service.Spec.Ports != nil {
|
if service.Spec.Ports != nil {
|
||||||
for _, port := range service.Spec.Ports {
|
for _, port := range service.Spec.Ports {
|
||||||
if port.Port > 0 {
|
if port.Port > 0 {
|
||||||
resolver.saveResolvedName(fmt.Sprintf("%s:%d", service.Spec.ClusterIP, port.Port), serviceHostname, event.Type)
|
resolver.saveResolvedName(fmt.Sprintf("%s:%d", service.Spec.ClusterIP, port.Port), serviceHostname, service.Namespace, event.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resolver.saveServiceIP(service.Spec.ClusterIP, serviceHostname, event.Type)
|
resolver.saveServiceIP(service.Spec.ClusterIP, serviceHostname, service.Namespace, event.Type)
|
||||||
}
|
}
|
||||||
if service.Status.LoadBalancer.Ingress != nil {
|
if service.Status.LoadBalancer.Ingress != nil {
|
||||||
for _, ingress := range service.Status.LoadBalancer.Ingress {
|
for _, ingress := range service.Status.LoadBalancer.Ingress {
|
||||||
resolver.saveResolvedName(ingress.IP, serviceHostname, event.Type)
|
resolver.saveResolvedName(ingress.IP, serviceHostname, service.Namespace, event.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
@@ -161,21 +166,22 @@ func (resolver *Resolver) watchServices(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (resolver *Resolver) saveResolvedName(key string, resolved string, eventType watch.EventType) {
|
func (resolver *Resolver) saveResolvedName(key string, resolved string, namespace string, eventType watch.EventType) {
|
||||||
if eventType == watch.Deleted {
|
if eventType == watch.Deleted {
|
||||||
resolver.nameMap.Remove(key)
|
resolver.nameMap.Remove(key)
|
||||||
logger.Log.Infof("setting %s=nil", key)
|
logger.Log.Infof("setting %s=nil", key)
|
||||||
} else {
|
} else {
|
||||||
resolver.nameMap.Set(key, resolved)
|
|
||||||
|
resolver.nameMap.Set(key, &ResolvedObjectInfo{FullAddress: resolved, Namespace: namespace})
|
||||||
logger.Log.Infof("setting %s=%s", key, resolved)
|
logger.Log.Infof("setting %s=%s", key, resolved)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (resolver *Resolver) saveServiceIP(key string, resolved string, eventType watch.EventType) {
|
func (resolver *Resolver) saveServiceIP(key string, resolved string, namespace string, eventType watch.EventType) {
|
||||||
if eventType == watch.Deleted {
|
if eventType == watch.Deleted {
|
||||||
resolver.serviceMap.Remove(key)
|
resolver.serviceMap.Remove(key)
|
||||||
} else {
|
} else {
|
||||||
resolver.serviceMap.Set(key, resolved)
|
resolver.nameMap.Set(key, &ResolvedObjectInfo{FullAddress: resolved, Namespace: namespace})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package servicemap
|
|||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/up9inc/mizu/shared"
|
|
||||||
"github.com/up9inc/mizu/shared/logger"
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
tapApi "github.com/up9inc/mizu/tap/api"
|
tapApi "github.com/up9inc/mizu/tap/api"
|
||||||
)
|
)
|
||||||
@@ -26,13 +25,14 @@ func GetInstance() ServiceMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type serviceMap struct {
|
type serviceMap struct {
|
||||||
config *shared.MizuAgentConfig
|
enabled bool
|
||||||
graph *graph
|
graph *graph
|
||||||
entriesProcessed int
|
entriesProcessed int
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServiceMap interface {
|
type ServiceMap interface {
|
||||||
SetConfig(config *shared.MizuAgentConfig)
|
Enable()
|
||||||
|
Disable()
|
||||||
IsEnabled() bool
|
IsEnabled() bool
|
||||||
NewTCPEntry(source *tapApi.TCP, destination *tapApi.TCP, protocol *tapApi.Protocol)
|
NewTCPEntry(source *tapApi.TCP, destination *tapApi.TCP, protocol *tapApi.Protocol)
|
||||||
GetStatus() ServiceMapStatus
|
GetStatus() ServiceMapStatus
|
||||||
@@ -46,7 +46,7 @@ type ServiceMap interface {
|
|||||||
|
|
||||||
func newServiceMap() *serviceMap {
|
func newServiceMap() *serviceMap {
|
||||||
return &serviceMap{
|
return &serviceMap{
|
||||||
config: nil,
|
enabled: false,
|
||||||
entriesProcessed: 0,
|
entriesProcessed: 0,
|
||||||
graph: newDirectedGraph(),
|
graph: newDirectedGraph(),
|
||||||
}
|
}
|
||||||
@@ -156,15 +156,17 @@ func (s *serviceMap) addEdge(u, v *entryData, p *tapApi.Protocol) {
|
|||||||
s.entriesProcessed++
|
s.entriesProcessed++
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceMap) SetConfig(config *shared.MizuAgentConfig) {
|
func (s *serviceMap) Enable() {
|
||||||
s.config = config
|
s.enabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serviceMap) Disable() {
|
||||||
|
s.Reset()
|
||||||
|
s.enabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceMap) IsEnabled() bool {
|
func (s *serviceMap) IsEnabled() bool {
|
||||||
if s.config != nil && s.config.ServiceMap {
|
return s.enabled
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceMap) NewTCPEntry(src *tapApi.TCP, dst *tapApi.TCP, p *tapApi.Protocol) {
|
func (s *serviceMap) NewTCPEntry(src *tapApi.TCP, dst *tapApi.TCP, p *tapApi.Protocol) {
|
||||||
@@ -172,20 +174,33 @@ func (s *serviceMap) NewTCPEntry(src *tapApi.TCP, dst *tapApi.TCP, p *tapApi.Pro
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
srcEntry := &entryData{
|
var srcEntry *entryData
|
||||||
key: key(src.IP),
|
var dstEntry *entryData
|
||||||
entry: src,
|
|
||||||
}
|
if len(src.Name) == 0 {
|
||||||
if len(srcEntry.entry.Name) == 0 {
|
srcEntry = &entryData{
|
||||||
|
key: key(src.IP),
|
||||||
|
entry: src,
|
||||||
|
}
|
||||||
srcEntry.entry.Name = UnresolvedNodeName
|
srcEntry.entry.Name = UnresolvedNodeName
|
||||||
|
} else {
|
||||||
|
srcEntry = &entryData{
|
||||||
|
key: key(src.Name),
|
||||||
|
entry: src,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dstEntry := &entryData{
|
if len(dst.Name) == 0 {
|
||||||
key: key(dst.IP),
|
dstEntry = &entryData{
|
||||||
entry: dst,
|
key: key(dst.IP),
|
||||||
}
|
entry: dst,
|
||||||
if len(dstEntry.entry.Name) == 0 {
|
}
|
||||||
dstEntry.entry.Name = UnresolvedNodeName
|
dstEntry.entry.Name = UnresolvedNodeName
|
||||||
|
} else {
|
||||||
|
dstEntry = &entryData{
|
||||||
|
key: key(dst.Name),
|
||||||
|
entry: dst,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.addEdge(srcEntry, dstEntry, p)
|
s.addEdge(srcEntry, dstEntry, p)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/up9inc/mizu/shared"
|
|
||||||
tapApi "github.com/up9inc/mizu/tap/api"
|
tapApi "github.com/up9inc/mizu/tap/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -96,9 +95,7 @@ func (s *ServiceMapDisabledSuite) SetupTest() {
|
|||||||
|
|
||||||
func (s *ServiceMapEnabledSuite) SetupTest() {
|
func (s *ServiceMapEnabledSuite) SetupTest() {
|
||||||
s.instance = GetInstance()
|
s.instance = GetInstance()
|
||||||
s.instance.SetConfig(&shared.MizuAgentConfig{
|
s.instance.Enable()
|
||||||
ServiceMap: true,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServiceMapDisabledSuite) TestServiceMapInstance() {
|
func (s *ServiceMapDisabledSuite) TestServiceMapInstance() {
|
||||||
@@ -268,9 +265,14 @@ func (s *ServiceMapEnabledSuite) TestServiceMap() {
|
|||||||
assert.LessOrEqual(node.Id, expectedNodeCount)
|
assert.LessOrEqual(node.Id, expectedNodeCount)
|
||||||
|
|
||||||
// entry
|
// entry
|
||||||
// node.Name is the key of the node, key = entry.IP
|
// node.Name is the key of the node, key = entry.Name by default
|
||||||
// entry.Name is the name of the service and could be unresolved
|
// entry.Name is the name of the service and could be unresolved
|
||||||
assert.Equal(node.Name, node.Entry.IP)
|
// when entry.Name is unresolved, key = entry.IP
|
||||||
|
if node.Entry.Name == UnresolvedNodeName {
|
||||||
|
assert.Equal(node.Name, node.Entry.IP)
|
||||||
|
} else {
|
||||||
|
assert.Equal(node.Name, node.Entry.Name)
|
||||||
|
}
|
||||||
assert.Equal(Port, node.Entry.Port)
|
assert.Equal(Port, node.Entry.Port)
|
||||||
assert.Equal(entryName, node.Entry.Name)
|
assert.Equal(entryName, node.Entry.Name)
|
||||||
|
|
||||||
@@ -320,16 +322,24 @@ func (s *ServiceMapEnabledSuite) TestServiceMap() {
|
|||||||
cdEdge := -1
|
cdEdge := -1
|
||||||
acEdge := -1
|
acEdge := -1
|
||||||
var validateEdge = func(edge ServiceMapEdge, sourceEntryName string, destEntryName string, protocolName string, protocolCount int) {
|
var validateEdge = func(edge ServiceMapEdge, sourceEntryName string, destEntryName string, protocolName string, protocolCount int) {
|
||||||
// source
|
// source node
|
||||||
assert.Contains(nodeIds, edge.Source.Id)
|
assert.Contains(nodeIds, edge.Source.Id)
|
||||||
assert.LessOrEqual(edge.Source.Id, expectedNodeCount)
|
assert.LessOrEqual(edge.Source.Id, expectedNodeCount)
|
||||||
assert.Equal(edge.Source.Name, edge.Source.Entry.IP)
|
if edge.Source.Entry.Name == UnresolvedNodeName {
|
||||||
|
assert.Equal(edge.Source.Name, edge.Source.Entry.IP)
|
||||||
|
} else {
|
||||||
|
assert.Equal(edge.Source.Name, edge.Source.Entry.Name)
|
||||||
|
}
|
||||||
assert.Equal(sourceEntryName, edge.Source.Entry.Name)
|
assert.Equal(sourceEntryName, edge.Source.Entry.Name)
|
||||||
|
|
||||||
// destination
|
// destination node
|
||||||
assert.Contains(nodeIds, edge.Destination.Id)
|
assert.Contains(nodeIds, edge.Destination.Id)
|
||||||
assert.LessOrEqual(edge.Destination.Id, expectedNodeCount)
|
assert.LessOrEqual(edge.Destination.Id, expectedNodeCount)
|
||||||
assert.Equal(edge.Destination.Name, edge.Destination.Entry.IP)
|
if edge.Destination.Entry.Name == UnresolvedNodeName {
|
||||||
|
assert.Equal(edge.Destination.Name, edge.Destination.Entry.IP)
|
||||||
|
} else {
|
||||||
|
assert.Equal(edge.Destination.Name, edge.Destination.Entry.Name)
|
||||||
|
}
|
||||||
assert.Equal(destEntryName, edge.Destination.Entry.Name)
|
assert.Equal(destEntryName, edge.Destination.Entry.Name)
|
||||||
|
|
||||||
// protocol
|
// protocol
|
||||||
|
|||||||
30
cli/Makefile
30
cli/Makefile
@@ -1,3 +1,9 @@
|
|||||||
|
SHELL=/bin/bash
|
||||||
|
|
||||||
|
.PHONY: help
|
||||||
|
.DEFAULT_GOAL := help
|
||||||
|
.ONESHELL:
|
||||||
|
|
||||||
SUFFIX=$(GOOS)_$(GOARCH)
|
SUFFIX=$(GOOS)_$(GOARCH)
|
||||||
COMMIT_HASH=$(shell git rev-parse HEAD)
|
COMMIT_HASH=$(shell git rev-parse HEAD)
|
||||||
GIT_BRANCH=$(shell git branch --show-current | tr '[:upper:]' '[:lower:]')
|
GIT_BRANCH=$(shell git branch --show-current | tr '[:upper:]' '[:lower:]')
|
||||||
@@ -5,9 +11,6 @@ GIT_VERSION=$(shell git branch --show-current | tr '[:upper:]' '[:lower:]')
|
|||||||
BUILD_TIMESTAMP=$(shell date +%s)
|
BUILD_TIMESTAMP=$(shell date +%s)
|
||||||
export VER?=0.0
|
export VER?=0.0
|
||||||
|
|
||||||
.PHONY: help
|
|
||||||
.DEFAULT_GOAL := help
|
|
||||||
|
|
||||||
help: ## This help.
|
help: ## This help.
|
||||||
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||||
|
|
||||||
@@ -16,15 +19,20 @@ install:
|
|||||||
|
|
||||||
build-debug: ## Build mizu CLI for debug
|
build-debug: ## Build mizu CLI for debug
|
||||||
export GCLFAGS='-gcflags="all=-N -l"'
|
export GCLFAGS='-gcflags="all=-N -l"'
|
||||||
${MAKE} build
|
${MAKE} build-base
|
||||||
|
|
||||||
build: ## Build mizu CLI binary (select platform via GOOS / GOARCH env variables).
|
build:
|
||||||
go build ${GCLFAGS} -ldflags="-X 'github.com/up9inc/mizu/cli/mizu.GitCommitHash=$(COMMIT_HASH)' \
|
export LDFLAGS_EXT='-s -w'
|
||||||
-X 'github.com/up9inc/mizu/cli/mizu.Branch=$(GIT_BRANCH)' \
|
${MAKE} build-base
|
||||||
-X 'github.com/up9inc/mizu/cli/mizu.BuildTimestamp=$(BUILD_TIMESTAMP)' \
|
|
||||||
-X 'github.com/up9inc/mizu/cli/mizu.Platform=$(SUFFIX)' \
|
build-base: ## Build mizu CLI binary (select platform via GOOS / GOARCH env variables).
|
||||||
-X 'github.com/up9inc/mizu/cli/mizu.Ver=$(VER)'" \
|
go build ${GCLFAGS} -ldflags="${LDFLAGS_EXT} \
|
||||||
-o bin/mizu_$(SUFFIX) mizu.go
|
-X 'github.com/up9inc/mizu/cli/mizu.GitCommitHash=$(COMMIT_HASH)' \
|
||||||
|
-X 'github.com/up9inc/mizu/cli/mizu.Branch=$(GIT_BRANCH)' \
|
||||||
|
-X 'github.com/up9inc/mizu/cli/mizu.BuildTimestamp=$(BUILD_TIMESTAMP)' \
|
||||||
|
-X 'github.com/up9inc/mizu/cli/mizu.Platform=$(SUFFIX)' \
|
||||||
|
-X 'github.com/up9inc/mizu/cli/mizu.Ver=$(VER)'" \
|
||||||
|
-o bin/mizu_$(SUFFIX) mizu.go
|
||||||
(cd bin && shasum -a 256 mizu_${SUFFIX} > mizu_${SUFFIX}.sha256)
|
(cd bin && shasum -a 256 mizu_${SUFFIX} > mizu_${SUFFIX}.sha256)
|
||||||
|
|
||||||
build-all: ## Build for all supported platforms.
|
build-all: ## Build for all supported platforms.
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ curl -Lo mizu https://github.com/up9inc/mizu/releases/download/_VER_/mizu_darwin
|
|||||||
|
|
||||||
**Mac** (AArch64/Apple M1 silicon)
|
**Mac** (AArch64/Apple M1 silicon)
|
||||||
```
|
```
|
||||||
curl -Lo mizu https://github.com/up9inc/mizu/releases/download/_VER_/mizu_darwin_arm64 && chmod 755 mizu
|
rm -f mizu && curl -Lo mizu https://github.com/up9inc/mizu/releases/download/_VER_/mizu_darwin_arm64 && chmod 755 mizu
|
||||||
```
|
```
|
||||||
|
|
||||||
**Linux** (x86-64)
|
**Linux** (x86-64)
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/up9inc/mizu/shared/kubernetes"
|
"github.com/up9inc/mizu/shared/kubernetes"
|
||||||
@@ -57,10 +59,8 @@ func (provider *Provider) TestConnection() error {
|
|||||||
|
|
||||||
func (provider *Provider) isReachable() (bool, error) {
|
func (provider *Provider) isReachable() (bool, error) {
|
||||||
echoUrl := fmt.Sprintf("%s/echo", provider.url)
|
echoUrl := fmt.Sprintf("%s/echo", provider.url)
|
||||||
if response, err := provider.client.Get(echoUrl); err != nil {
|
if _, err := provider.get(echoUrl); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
} else if response.StatusCode != 200 {
|
|
||||||
return false, fmt.Errorf("invalid status code %v", response.StatusCode)
|
|
||||||
} else {
|
} else {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
@@ -72,10 +72,8 @@ func (provider *Provider) ReportTapperStatus(tapperStatus shared.TapperStatus) e
|
|||||||
if jsonValue, err := json.Marshal(tapperStatus); err != nil {
|
if jsonValue, err := json.Marshal(tapperStatus); err != nil {
|
||||||
return fmt.Errorf("failed Marshal the tapper status %w", err)
|
return fmt.Errorf("failed Marshal the tapper status %w", err)
|
||||||
} else {
|
} else {
|
||||||
if response, err := provider.client.Post(tapperStatusUrl, "application/json", bytes.NewBuffer(jsonValue)); err != nil {
|
if _, err := provider.post(tapperStatusUrl, "application/json", bytes.NewBuffer(jsonValue)); err != nil {
|
||||||
return fmt.Errorf("failed sending to API server the tapped pods %w", err)
|
return fmt.Errorf("failed sending to API server the tapped pods %w", err)
|
||||||
} else if response.StatusCode != 200 {
|
|
||||||
return fmt.Errorf("failed sending to API server the tapper status, response status code %v", response.StatusCode)
|
|
||||||
} else {
|
} else {
|
||||||
logger.Log.Debugf("Reported to server API about tapper status: %v", tapperStatus)
|
logger.Log.Debugf("Reported to server API about tapper status: %v", tapperStatus)
|
||||||
return nil
|
return nil
|
||||||
@@ -91,10 +89,8 @@ func (provider *Provider) ReportTappedPods(pods []core.Pod) error {
|
|||||||
if jsonValue, err := json.Marshal(podInfos); err != nil {
|
if jsonValue, err := json.Marshal(podInfos); err != nil {
|
||||||
return fmt.Errorf("failed Marshal the tapped pods %w", err)
|
return fmt.Errorf("failed Marshal the tapped pods %w", err)
|
||||||
} else {
|
} else {
|
||||||
if response, err := provider.client.Post(tappedPodsUrl, "application/json", bytes.NewBuffer(jsonValue)); err != nil {
|
if _, err := provider.post(tappedPodsUrl, "application/json", bytes.NewBuffer(jsonValue)); err != nil {
|
||||||
return fmt.Errorf("failed sending to API server the tapped pods %w", err)
|
return fmt.Errorf("failed sending to API server the tapped pods %w", err)
|
||||||
} else if response.StatusCode != 200 {
|
|
||||||
return fmt.Errorf("failed sending to API server the tapped pods, response status code %v", response.StatusCode)
|
|
||||||
} else {
|
} else {
|
||||||
logger.Log.Debugf("Reported to server API about %d taped pods successfully", len(podInfos))
|
logger.Log.Debugf("Reported to server API about %d taped pods successfully", len(podInfos))
|
||||||
return nil
|
return nil
|
||||||
@@ -105,11 +101,9 @@ func (provider *Provider) ReportTappedPods(pods []core.Pod) error {
|
|||||||
func (provider *Provider) GetGeneralStats() (map[string]interface{}, error) {
|
func (provider *Provider) GetGeneralStats() (map[string]interface{}, error) {
|
||||||
generalStatsUrl := fmt.Sprintf("%s/status/general", provider.url)
|
generalStatsUrl := fmt.Sprintf("%s/status/general", provider.url)
|
||||||
|
|
||||||
response, requestErr := provider.client.Get(generalStatsUrl)
|
response, requestErr := provider.get(generalStatsUrl)
|
||||||
if requestErr != nil {
|
if requestErr != nil {
|
||||||
return nil, fmt.Errorf("failed to get general stats for telemetry, err: %w", requestErr)
|
return nil, fmt.Errorf("failed to get general stats for telemetry, err: %w", requestErr)
|
||||||
} else if response.StatusCode != 200 {
|
|
||||||
return nil, fmt.Errorf("failed to get general stats for telemetry, status code: %v", response.StatusCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
@@ -132,7 +126,7 @@ func (provider *Provider) GetVersion() (string, error) {
|
|||||||
Method: http.MethodGet,
|
Method: http.MethodGet,
|
||||||
URL: versionUrl,
|
URL: versionUrl,
|
||||||
}
|
}
|
||||||
statusResp, err := provider.client.Do(req)
|
statusResp, err := provider.do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -145,3 +139,40 @@ func (provider *Provider) GetVersion() (string, error) {
|
|||||||
|
|
||||||
return versionResponse.Ver, nil
|
return versionResponse.Ver, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When err is nil, resp always contains a non-nil resp.Body.
|
||||||
|
// Caller should close resp.Body when done reading from it.
|
||||||
|
func (provider *Provider) get(url string) (*http.Response, error) {
|
||||||
|
return provider.checkError(provider.client.Get(url))
|
||||||
|
}
|
||||||
|
|
||||||
|
// When err is nil, resp always contains a non-nil resp.Body.
|
||||||
|
// Caller should close resp.Body when done reading from it.
|
||||||
|
func (provider *Provider) post(url, contentType string, body io.Reader) (*http.Response, error) {
|
||||||
|
return provider.checkError(provider.client.Post(url, contentType, body))
|
||||||
|
}
|
||||||
|
|
||||||
|
// When err is nil, resp always contains a non-nil resp.Body.
|
||||||
|
// Caller should close resp.Body when done reading from it.
|
||||||
|
func (provider *Provider) do(req *http.Request) (*http.Response, error) {
|
||||||
|
return provider.checkError(provider.client.Do(req))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *Provider) checkError(response *http.Response, errInOperation error) (*http.Response, error) {
|
||||||
|
if (errInOperation != nil) {
|
||||||
|
return response, errInOperation
|
||||||
|
// Check only if status != 200 (and not status >= 300). Agent APIs return only 200 on success.
|
||||||
|
} else if response.StatusCode != http.StatusOK {
|
||||||
|
body, err := ioutil.ReadAll(response.Body)
|
||||||
|
response.Body.Close()
|
||||||
|
response.Body = io.NopCloser(bytes.NewBuffer(body)) // rewind
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
errorMsg := strings.ReplaceAll((string(body)), "\n", ";")
|
||||||
|
return response, fmt.Errorf("got response with status code: %d, body: %s", response.StatusCode, errorMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,15 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
core "k8s.io/api/core/v1"
|
||||||
|
rbac "k8s.io/api/rbac/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/up9inc/mizu/cli/apiserver"
|
"github.com/up9inc/mizu/cli/apiserver"
|
||||||
"github.com/up9inc/mizu/cli/config"
|
"github.com/up9inc/mizu/cli/config"
|
||||||
@@ -13,8 +20,13 @@ import (
|
|||||||
"github.com/up9inc/mizu/shared/semver"
|
"github.com/up9inc/mizu/shared/semver"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed permissionFiles
|
||||||
|
embedFS embed.FS
|
||||||
|
)
|
||||||
|
|
||||||
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 +37,22 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
if checkPassed {
|
if checkPassed {
|
||||||
checkPassed = checkK8sResources(ctx, kubernetesProvider, isInstallCommand)
|
checkPassed = checkImagePullInCluster(ctx, kubernetesProvider)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if checkPassed {
|
||||||
|
checkPassed = checkK8sResources(ctx, kubernetesProvider)
|
||||||
|
}
|
||||||
|
|
||||||
if checkPassed {
|
if checkPassed {
|
||||||
checkPassed = checkServerConnection(kubernetesProvider)
|
checkPassed = checkServerConnection(kubernetesProvider)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if checkPassed {
|
if checkPassed {
|
||||||
@@ -65,27 +82,6 @@ func checkKubernetesApi() (*kubernetes.Provider, *semver.SemVersion, bool) {
|
|||||||
return kubernetesProvider, kubernetesVersion, true
|
return kubernetesProvider, kubernetesVersion, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkMizuMode(ctx context.Context, kubernetesProvider *kubernetes.Provider) (bool, bool) {
|
|
||||||
logger.Log.Infof("\nmode\n--------------------")
|
|
||||||
|
|
||||||
if exist, err := kubernetesProvider.DoesDeploymentExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.ApiServerPodName); err != nil {
|
|
||||||
logger.Log.Errorf("%v can't check mizu command, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
|
|
||||||
return false, false
|
|
||||||
} else if exist {
|
|
||||||
logger.Log.Infof("%v mizu running with install command", fmt.Sprintf(uiUtils.Green, "√"))
|
|
||||||
return true, true
|
|
||||||
} else if exist, err = kubernetesProvider.DoesPodExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.ApiServerPodName); err != nil {
|
|
||||||
logger.Log.Errorf("%v can't check mizu command, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
|
|
||||||
return false, false
|
|
||||||
} else if exist {
|
|
||||||
logger.Log.Infof("%v mizu running with tap command", fmt.Sprintf(uiUtils.Green, "√"))
|
|
||||||
return true, false
|
|
||||||
} else {
|
|
||||||
logger.Log.Infof("%v mizu is not running", fmt.Sprintf(uiUtils.Red, "✗"))
|
|
||||||
return false, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkKubernetesVersion(kubernetesVersion *semver.SemVersion) bool {
|
func checkKubernetesVersion(kubernetesVersion *semver.SemVersion) bool {
|
||||||
logger.Log.Infof("\nkubernetes-version\n--------------------")
|
logger.Log.Infof("\nkubernetes-version\n--------------------")
|
||||||
|
|
||||||
@@ -169,7 +165,7 @@ func checkPortForward(serverUrl string, kubernetesProvider *kubernetes.Provider)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkK8sResources(ctx context.Context, kubernetesProvider *kubernetes.Provider, isInstallCommand bool) bool {
|
func checkK8sResources(ctx context.Context, kubernetesProvider *kubernetes.Provider) bool {
|
||||||
logger.Log.Infof("\nk8s-components\n--------------------")
|
logger.Log.Infof("\nk8s-components\n--------------------")
|
||||||
|
|
||||||
exist, err := kubernetesProvider.DoesNamespaceExist(ctx, config.Config.MizuResourcesNamespace)
|
exist, err := kubernetesProvider.DoesNamespaceExist(ctx, config.Config.MizuResourcesNamespace)
|
||||||
@@ -198,52 +194,27 @@ func checkK8sResources(ctx context.Context, kubernetesProvider *kubernetes.Provi
|
|||||||
exist, err = kubernetesProvider.DoesServiceExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.ApiServerPodName)
|
exist, err = kubernetesProvider.DoesServiceExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.ApiServerPodName)
|
||||||
allResourcesExist = checkResourceExist(kubernetes.ApiServerPodName, "service", exist, err) && allResourcesExist
|
allResourcesExist = checkResourceExist(kubernetes.ApiServerPodName, "service", exist, err) && allResourcesExist
|
||||||
|
|
||||||
if isInstallCommand {
|
allResourcesExist = checkPodResourcesExist(ctx, kubernetesProvider) && allResourcesExist
|
||||||
allResourcesExist = checkInstallResourcesExist(ctx, kubernetesProvider) && allResourcesExist
|
|
||||||
} else {
|
|
||||||
allResourcesExist = checkTapResourcesExist(ctx, kubernetesProvider) && allResourcesExist
|
|
||||||
}
|
|
||||||
|
|
||||||
return allResourcesExist
|
return allResourcesExist
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkInstallResourcesExist(ctx context.Context, kubernetesProvider *kubernetes.Provider) bool {
|
func checkPodResourcesExist(ctx context.Context, kubernetesProvider *kubernetes.Provider) bool {
|
||||||
exist, err := kubernetesProvider.DoesRoleExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.DaemonRoleName)
|
if pods, err := kubernetesProvider.ListPodsByAppLabel(ctx, config.Config.MizuResourcesNamespace, kubernetes.ApiServerPodName); err != nil {
|
||||||
installResourcesExist := checkResourceExist(kubernetes.DaemonRoleName, "role", exist, err)
|
logger.Log.Errorf("%v error checking if '%v' pod is running, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), kubernetes.ApiServerPodName, err)
|
||||||
|
|
||||||
exist, err = kubernetesProvider.DoesRoleBindingExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.DaemonRoleBindingName)
|
|
||||||
installResourcesExist = checkResourceExist(kubernetes.DaemonRoleBindingName, "role binding", exist, err) && installResourcesExist
|
|
||||||
|
|
||||||
exist, err = kubernetesProvider.DoesPersistentVolumeClaimExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.PersistentVolumeClaimName)
|
|
||||||
installResourcesExist = checkResourceExist(kubernetes.PersistentVolumeClaimName, "persistent volume claim", exist, err) && installResourcesExist
|
|
||||||
|
|
||||||
exist, err = kubernetesProvider.DoesDeploymentExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.ApiServerPodName)
|
|
||||||
installResourcesExist = checkResourceExist(kubernetes.ApiServerPodName, "deployment", exist, err) && installResourcesExist
|
|
||||||
|
|
||||||
return installResourcesExist
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkTapResourcesExist(ctx context.Context, kubernetesProvider *kubernetes.Provider) bool {
|
|
||||||
exist, err := kubernetesProvider.DoesPodExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.ApiServerPodName)
|
|
||||||
tapResourcesExist := checkResourceExist(kubernetes.ApiServerPodName, "pod", exist, err)
|
|
||||||
|
|
||||||
if !tapResourcesExist {
|
|
||||||
return false
|
return false
|
||||||
}
|
} else if len(pods) == 0 {
|
||||||
|
logger.Log.Errorf("%v '%v' pod doesn't exist", fmt.Sprintf(uiUtils.Red, "✗"), kubernetes.ApiServerPodName)
|
||||||
if pod, err := kubernetesProvider.GetPod(ctx, config.Config.MizuResourcesNamespace, kubernetes.ApiServerPodName); err != nil {
|
|
||||||
logger.Log.Errorf("%v error checking if '%v' pod exists, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), kubernetes.ApiServerPodName, err)
|
|
||||||
return false
|
return false
|
||||||
} else if kubernetes.IsPodRunning(pod) {
|
} else if !kubernetes.IsPodRunning(&pods[0]) {
|
||||||
logger.Log.Infof("%v '%v' pod running", fmt.Sprintf(uiUtils.Green, "√"), kubernetes.ApiServerPodName)
|
|
||||||
} else {
|
|
||||||
logger.Log.Errorf("%v '%v' pod not running", fmt.Sprintf(uiUtils.Red, "✗"), kubernetes.ApiServerPodName)
|
logger.Log.Errorf("%v '%v' pod not running", fmt.Sprintf(uiUtils.Red, "✗"), kubernetes.ApiServerPodName)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
tapperRegex := regexp.MustCompile(fmt.Sprintf("^%s.*", kubernetes.TapperPodName))
|
logger.Log.Infof("%v '%v' pod running", fmt.Sprintf(uiUtils.Green, "√"), kubernetes.ApiServerPodName)
|
||||||
if pods, err := kubernetesProvider.ListAllPodsMatchingRegex(ctx, tapperRegex, []string{config.Config.MizuResourcesNamespace}); err != nil {
|
|
||||||
logger.Log.Errorf("%v error listing '%v' pods, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), kubernetes.TapperPodName, err)
|
if pods, err := kubernetesProvider.ListPodsByAppLabel(ctx, config.Config.MizuResourcesNamespace, kubernetes.TapperPodName); err != nil {
|
||||||
|
logger.Log.Errorf("%v error checking if '%v' pods are running, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), kubernetes.TapperPodName, err)
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
tappers := 0
|
tappers := 0
|
||||||
@@ -273,9 +244,182 @@ 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
|
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 = "permissionFiles/permissions-ns-tap.yaml"
|
||||||
|
} else {
|
||||||
|
filePath = "permissionFiles/permissions-all-namespaces-tap.yaml"
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := embedFS.ReadFile(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
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkImagePullInCluster(ctx context.Context, kubernetesProvider *kubernetes.Provider) bool {
|
||||||
|
logger.Log.Infof("\nimage-pull-in-cluster\n--------------------")
|
||||||
|
|
||||||
|
podName := "image-pull-in-cluster"
|
||||||
|
|
||||||
|
defer removeImagePullInClusterResources(ctx, kubernetesProvider, podName)
|
||||||
|
if err := createImagePullInClusterResources(ctx, kubernetesProvider, podName); err != nil {
|
||||||
|
logger.Log.Errorf("%v error while creating image pull in cluster resources, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := checkImagePulled(ctx, kubernetesProvider, podName); err != nil {
|
||||||
|
logger.Log.Errorf("%v cluster is not able to pull mizu containers from docker hub, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Log.Infof("%v cluster is able to pull mizu containers from docker hub", fmt.Sprintf(uiUtils.Green, "√"))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkImagePulled(ctx context.Context, kubernetesProvider *kubernetes.Provider, podName string) error {
|
||||||
|
podExactRegex := regexp.MustCompile(fmt.Sprintf("^%s$", podName))
|
||||||
|
podWatchHelper := kubernetes.NewPodWatchHelper(kubernetesProvider, podExactRegex)
|
||||||
|
eventChan, errorChan := kubernetes.FilteredWatch(ctx, podWatchHelper, []string{config.Config.MizuResourcesNamespace}, podWatchHelper)
|
||||||
|
|
||||||
|
timeAfter := time.After(30 * time.Second)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case wEvent, ok := <-eventChan:
|
||||||
|
if !ok {
|
||||||
|
eventChan = nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pod, err := wEvent.ToPod()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if pod.Status.Phase == core.PodRunning {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case err, ok := <-errorChan:
|
||||||
|
if !ok {
|
||||||
|
errorChan = nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
case <-timeAfter:
|
||||||
|
return fmt.Errorf("image not pulled in time")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeImagePullInClusterResources(ctx context.Context, kubernetesProvider *kubernetes.Provider, podName string) {
|
||||||
|
if err := kubernetesProvider.RemovePod(ctx, config.Config.MizuResourcesNamespace, podName); err != nil {
|
||||||
|
logger.Log.Debugf("error while removing image pull in cluster resources, err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !config.Config.IsNsRestrictedMode() {
|
||||||
|
if err := kubernetesProvider.RemoveNamespace(ctx, config.Config.MizuResourcesNamespace); err != nil {
|
||||||
|
logger.Log.Debugf("error while removing image pull in cluster resources, err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createImagePullInClusterResources(ctx context.Context, kubernetesProvider *kubernetes.Provider, podName string) error {
|
||||||
|
if !config.Config.IsNsRestrictedMode() {
|
||||||
|
if _, err := kubernetesProvider.CreateNamespace(ctx, config.Config.MizuResourcesNamespace); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var zero int64
|
||||||
|
pod := &core.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: podName,
|
||||||
|
},
|
||||||
|
Spec: core.PodSpec{
|
||||||
|
Containers: []core.Container{
|
||||||
|
{
|
||||||
|
Name: "probe",
|
||||||
|
Image: "up9inc/busybox",
|
||||||
|
ImagePullPolicy: "Always",
|
||||||
|
Command: []string{"cat"},
|
||||||
|
Stdin: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TerminationGracePeriodSeconds: &zero,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := kubernetesProvider.CreatePod(ctx, config.Config.MizuResourcesNamespace, pod); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/up9inc/mizu/cli/config"
|
|
||||||
"github.com/up9inc/mizu/cli/telemetry"
|
"github.com/up9inc/mizu/cli/telemetry"
|
||||||
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
var installCmd = &cobra.Command{
|
var installCmd = &cobra.Command{
|
||||||
@@ -12,13 +11,13 @@ var installCmd = &cobra.Command{
|
|||||||
Short: "Installs mizu components",
|
Short: "Installs mizu components",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
go telemetry.ReportRun("install", nil)
|
go telemetry.ReportRun("install", nil)
|
||||||
runMizuInstall()
|
logger.Log.Infof("This command has been deprecated, please use helm as described below.\n\n")
|
||||||
return nil
|
|
||||||
},
|
logger.Log.Infof("To install stable build of Mizu on your cluster using helm, run the following command:")
|
||||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
logger.Log.Infof(" helm install mizu up9mizu --repo https://static.up9.com/mizu/helm --namespace=mizu --create-namespace\n\n")
|
||||||
if config.Config.IsNsRestrictedMode() {
|
|
||||||
return fmt.Errorf("install is not supported in restricted namespace mode")
|
logger.Log.Infof("To install development build of Mizu on your cluster using helm, run the following command:")
|
||||||
}
|
logger.Log.Infof(" helm install mizu up9mizu --repo https://static.up9.com/mizu/helm-develop --namespace=mizu --create-namespace\n")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
@@ -27,4 +26,3 @@ var installCmd = &cobra.Command{
|
|||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(installCmd)
|
rootCmd.AddCommand(installCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/creasty/defaults"
|
|
||||||
"github.com/up9inc/mizu/cli/config"
|
|
||||||
"github.com/up9inc/mizu/cli/errormessage"
|
|
||||||
"github.com/up9inc/mizu/cli/resources"
|
|
||||||
"github.com/up9inc/mizu/cli/uiUtils"
|
|
||||||
"github.com/up9inc/mizu/shared"
|
|
||||||
"github.com/up9inc/mizu/shared/logger"
|
|
||||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
func runMizuInstall() {
|
|
||||||
kubernetesProvider, err := getKubernetesProviderForCli()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel() // cancel will be called when this function exits
|
|
||||||
|
|
||||||
var serializedValidationRules string
|
|
||||||
var serializedContract string
|
|
||||||
|
|
||||||
var defaultMaxEntriesDBSizeBytes int64 = 200 * 1000 * 1000
|
|
||||||
|
|
||||||
defaultResources := shared.Resources{}
|
|
||||||
if err := defaults.Set(&defaultResources); err != nil {
|
|
||||||
logger.Log.Debug(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
mizuAgentConfig := getInstallMizuAgentConfig(defaultMaxEntriesDBSizeBytes, defaultResources)
|
|
||||||
serializedMizuConfig, err := getSerializedMizuAgentConfig(mizuAgentConfig)
|
|
||||||
if err != nil {
|
|
||||||
logger.Log.Errorf(uiUtils.Error, fmt.Sprintf("Error serializing mizu config: %v", errormessage.FormatError(err)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = resources.CreateInstallMizuResources(ctx, kubernetesProvider, serializedValidationRules,
|
|
||||||
serializedContract, serializedMizuConfig, config.Config.IsNsRestrictedMode(),
|
|
||||||
config.Config.MizuResourcesNamespace, config.Config.AgentImage,
|
|
||||||
config.Config.KratosImage, config.Config.KetoImage,
|
|
||||||
nil, defaultMaxEntriesDBSizeBytes, defaultResources, config.Config.ImagePullPolicy(),
|
|
||||||
config.Config.LogLevel(), false); err != nil {
|
|
||||||
var statusError *k8serrors.StatusError
|
|
||||||
if errors.As(err, &statusError) && (statusError.ErrStatus.Reason == metav1.StatusReasonAlreadyExists) {
|
|
||||||
logger.Log.Info("Mizu is already running in this namespace, run `mizu clean` to remove the currently running Mizu instance")
|
|
||||||
} else {
|
|
||||||
defer resources.CleanUpMizuResources(ctx, cancel, kubernetesProvider, config.Config.IsNsRestrictedMode(), config.Config.MizuResourcesNamespace)
|
|
||||||
logger.Log.Errorf(uiUtils.Error, fmt.Sprintf("Error creating resources: %v", errormessage.FormatError(err)))
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Log.Infof(uiUtils.Magenta, "Installation completed, run `mizu view` to connect to the mizu daemon instance")
|
|
||||||
}
|
|
||||||
|
|
||||||
func getInstallMizuAgentConfig(maxDBSizeBytes int64, tapperResources shared.Resources) *shared.MizuAgentConfig {
|
|
||||||
mizuAgentConfig := shared.MizuAgentConfig{
|
|
||||||
MaxDBSizeBytes: maxDBSizeBytes,
|
|
||||||
AgentImage: config.Config.AgentImage,
|
|
||||||
PullPolicy: config.Config.ImagePullPolicyStr,
|
|
||||||
LogLevel: config.Config.LogLevel(),
|
|
||||||
TapperResources: tapperResources,
|
|
||||||
MizuResourcesNamespace: config.Config.MizuResourcesNamespace,
|
|
||||||
AgentDatabasePath: shared.DataDirPath,
|
|
||||||
StandaloneMode: true,
|
|
||||||
ServiceMap: config.Config.ServiceMap,
|
|
||||||
OAS: config.Config.OAS,
|
|
||||||
Elastic: config.Config.Elastic,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &mizuAgentConfig
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# This example shows permissions that enrich the logs with additional info
|
||||||
|
kind: ClusterRole
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: mizu-runner-debug-clusterrole
|
||||||
|
rules:
|
||||||
|
- apiGroups: ["events.k8s.io"]
|
||||||
|
resources: ["events"]
|
||||||
|
verbs: ["watch"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods"]
|
||||||
|
verbs: ["get"]
|
||||||
|
---
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: mizu-runner-debug-clusterrolebindings
|
||||||
|
subjects:
|
||||||
|
- kind: User
|
||||||
|
name: user-with-clusterwide-access
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
roleRef:
|
||||||
|
kind: ClusterRole
|
||||||
|
name: mizu-runner-debug-clusterrole
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# This example shows permissions that are required for Mizu to resolve IPs to service names
|
||||||
|
kind: ClusterRole
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: mizu-resolver-clusterrole
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["serviceaccounts"]
|
||||||
|
verbs: ["get", "create"]
|
||||||
|
- apiGroups: ["rbac.authorization.k8s.io"]
|
||||||
|
resources: ["clusterroles"]
|
||||||
|
verbs: ["get", "list", "create", "delete"]
|
||||||
|
- apiGroups: ["rbac.authorization.k8s.io"]
|
||||||
|
resources: ["clusterrolebindings"]
|
||||||
|
verbs: ["get", "list", "create", "delete"]
|
||||||
|
- apiGroups: ["", "apps", "extensions"]
|
||||||
|
resources: ["pods"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
- apiGroups: ["", "apps", "extensions"]
|
||||||
|
resources: ["services"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
- apiGroups: ["", "apps", "extensions"]
|
||||||
|
resources: ["endpoints"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
---
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: mizu-resolver-clusterrolebindings
|
||||||
|
subjects:
|
||||||
|
- kind: User
|
||||||
|
name: user-with-clusterwide-access
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
roleRef:
|
||||||
|
kind: ClusterRole
|
||||||
|
name: mizu-resolver-clusterrole
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
# This example shows the roles required for a user to be able to use Mizu in all namespaces with IP resolution disabled.
|
# This example shows the permissions that are required in order to run the `mizu tap` command
|
||||||
# (Traffic will be recorded, but Mizu will not translate IP addresses to names)
|
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
metadata:
|
metadata:
|
||||||
@@ -7,25 +6,25 @@ metadata:
|
|||||||
rules:
|
rules:
|
||||||
- apiGroups: [""]
|
- apiGroups: [""]
|
||||||
resources: ["pods"]
|
resources: ["pods"]
|
||||||
verbs: ["list", "watch", "create", "delete"]
|
verbs: ["list", "watch", "create"]
|
||||||
- apiGroups: [""]
|
- apiGroups: [""]
|
||||||
resources: ["services"]
|
resources: ["services"]
|
||||||
verbs: ["create", "delete"]
|
verbs: ["get", "create"]
|
||||||
- apiGroups: ["apps"]
|
- apiGroups: ["apps"]
|
||||||
resources: ["daemonsets"]
|
resources: ["daemonsets"]
|
||||||
verbs: ["create", "patch", "delete"]
|
verbs: ["create", "patch"]
|
||||||
- apiGroups: [""]
|
- apiGroups: [""]
|
||||||
resources: ["namespaces"]
|
resources: ["namespaces"]
|
||||||
verbs: ["get", "list", "watch", "create", "delete"]
|
verbs: ["list", "watch", "create", "delete"]
|
||||||
- apiGroups: [""]
|
- apiGroups: [""]
|
||||||
resources: ["services/proxy"]
|
resources: ["services/proxy"]
|
||||||
verbs: ["get"]
|
verbs: ["get", "create"]
|
||||||
- apiGroups: [""]
|
- apiGroups: [""]
|
||||||
resources: ["configmaps"]
|
resources: ["configmaps"]
|
||||||
verbs: ["get", "create", "delete"]
|
verbs: ["create"]
|
||||||
- apiGroups: ["events.k8s.io"]
|
- apiGroups: [""]
|
||||||
resources: ["events"]
|
resources: ["pods/log"]
|
||||||
verbs: ["list", "watch"]
|
verbs: ["get"]
|
||||||
---
|
---
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
@@ -33,7 +32,7 @@ metadata:
|
|||||||
name: mizu-runner-clusterrolebindings
|
name: mizu-runner-clusterrolebindings
|
||||||
subjects:
|
subjects:
|
||||||
- kind: User
|
- kind: User
|
||||||
name: user1
|
name: user-with-clusterwide-access
|
||||||
apiGroup: rbac.authorization.k8s.io
|
apiGroup: rbac.authorization.k8s.io
|
||||||
roleRef:
|
roleRef:
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
25
cli/cmd/permissionFiles/permissions-ns-debug-optional.yaml
Normal file
25
cli/cmd/permissionFiles/permissions-ns-debug-optional.yaml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# This example shows permissions that enrich the logs with additional info in namespace-restricted mode
|
||||||
|
kind: Role
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: mizu-runner-debug-role
|
||||||
|
rules:
|
||||||
|
- apiGroups: ["events.k8s.io"]
|
||||||
|
resources: ["events"]
|
||||||
|
verbs: ["watch"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods"]
|
||||||
|
verbs: ["get"]
|
||||||
|
---
|
||||||
|
kind: RoleBinding
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: mizu-runner-debug-rolebindings
|
||||||
|
subjects:
|
||||||
|
- kind: User
|
||||||
|
name: user-with-restricted-access
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
roleRef:
|
||||||
|
kind: Role
|
||||||
|
name: mizu-runner-debug-role
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# This example shows permissions that are required for Mizu to resolve IPs to service names in namespace-restricted mode
|
||||||
|
kind: Role
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: mizu-resolver-role
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["serviceaccounts"]
|
||||||
|
verbs: ["get", "list", "create", "delete"]
|
||||||
|
- apiGroups: ["rbac.authorization.k8s.io"]
|
||||||
|
resources: ["roles"]
|
||||||
|
verbs: ["get", "list", "create", "delete"]
|
||||||
|
- apiGroups: ["rbac.authorization.k8s.io"]
|
||||||
|
resources: ["rolebindings"]
|
||||||
|
verbs: ["get", "list", "create", "delete"]
|
||||||
|
- apiGroups: ["", "apps", "extensions"]
|
||||||
|
resources: ["pods"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
- apiGroups: ["", "apps", "extensions"]
|
||||||
|
resources: ["services"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
- apiGroups: ["", "apps", "extensions"]
|
||||||
|
resources: ["endpoints"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
---
|
||||||
|
kind: RoleBinding
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: mizu-resolver-rolebindings
|
||||||
|
subjects:
|
||||||
|
- kind: User
|
||||||
|
name: user-with-restricted-access
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
roleRef:
|
||||||
|
kind: Role
|
||||||
|
name: mizu-resolver-role
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
@@ -1,37 +1,35 @@
|
|||||||
# This example shows the roles required for a user to be able to use Mizu in a single namespace with IP resolution disabled.
|
# This example shows the permissions that are required in order to run the `mizu tap` command in namespace-restricted mode
|
||||||
kind: Role
|
kind: Role
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
metadata:
|
metadata:
|
||||||
name: mizu-runner-role
|
name: mizu-runner-role
|
||||||
namespace: user1
|
|
||||||
rules:
|
rules:
|
||||||
- apiGroups: [""]
|
- apiGroups: [""]
|
||||||
resources: ["pods"]
|
resources: ["pods"]
|
||||||
verbs: ["get", "list", "watch", "create", "delete"]
|
verbs: ["list", "watch", "create"]
|
||||||
- apiGroups: [""]
|
- apiGroups: [""]
|
||||||
resources: ["services"]
|
resources: ["services"]
|
||||||
verbs: ["get", "create", "delete"]
|
verbs: ["get", "create", "delete"]
|
||||||
- apiGroups: ["apps"]
|
- apiGroups: ["apps"]
|
||||||
resources: ["daemonsets"]
|
resources: ["daemonsets"]
|
||||||
verbs: ["get", "create", "patch", "delete"]
|
verbs: ["create", "patch", "delete"]
|
||||||
- apiGroups: [""]
|
- apiGroups: [""]
|
||||||
resources: ["services/proxy"]
|
resources: ["services/proxy"]
|
||||||
verbs: ["get"]
|
verbs: ["get", "create", "delete"]
|
||||||
- apiGroups: [""]
|
- apiGroups: [""]
|
||||||
resources: ["configmaps"]
|
resources: ["configmaps"]
|
||||||
verbs: ["get", "create", "delete"]
|
verbs: ["create", "delete"]
|
||||||
- apiGroups: ["events.k8s.io"]
|
- apiGroups: [""]
|
||||||
resources: ["events"]
|
resources: ["pods/log"]
|
||||||
verbs: ["list", "watch"]
|
verbs: ["get"]
|
||||||
---
|
---
|
||||||
kind: RoleBinding
|
kind: RoleBinding
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
metadata:
|
metadata:
|
||||||
name: mizu-runner-rolebindings
|
name: mizu-runner-rolebindings
|
||||||
namespace: user1
|
|
||||||
subjects:
|
subjects:
|
||||||
- kind: User
|
- kind: User
|
||||||
name: user1
|
name: user-with-restricted-access
|
||||||
apiGroup: rbac.authorization.k8s.io
|
apiGroup: rbac.authorization.k8s.io
|
||||||
roleRef:
|
roleRef:
|
||||||
kind: Role
|
kind: Role
|
||||||
@@ -120,4 +120,5 @@ func init() {
|
|||||||
tapCmd.Flags().String(configStructs.EnforcePolicyFile, defaultTapConfig.EnforcePolicyFile, "Yaml file path with policy rules")
|
tapCmd.Flags().String(configStructs.EnforcePolicyFile, defaultTapConfig.EnforcePolicyFile, "Yaml file path with policy rules")
|
||||||
tapCmd.Flags().String(configStructs.ContractFile, defaultTapConfig.ContractFile, "OAS/Swagger file to validate to monitor the contracts")
|
tapCmd.Flags().String(configStructs.ContractFile, defaultTapConfig.ContractFile, "OAS/Swagger file to validate to monitor the contracts")
|
||||||
tapCmd.Flags().Bool(configStructs.ServiceMeshName, defaultTapConfig.ServiceMesh, "Record decrypted traffic if the cluster is configured with a service mesh and with mtls")
|
tapCmd.Flags().Bool(configStructs.ServiceMeshName, defaultTapConfig.ServiceMesh, "Record decrypted traffic if the cluster is configured with a service mesh and with mtls")
|
||||||
|
tapCmd.Flags().Bool(configStructs.TlsName, defaultTapConfig.Tls, "Record tls traffic")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ func getTapMizuAgentConfig() *shared.MizuAgentConfig {
|
|||||||
AgentDatabasePath: shared.DataDirPath,
|
AgentDatabasePath: shared.DataDirPath,
|
||||||
ServiceMap: config.Config.ServiceMap,
|
ServiceMap: config.Config.ServiceMap,
|
||||||
OAS: config.Config.OAS,
|
OAS: config.Config.OAS,
|
||||||
|
Telemetry: config.Config.Telemetry,
|
||||||
Elastic: config.Config.Elastic,
|
Elastic: config.Config.Elastic,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,6 +201,7 @@ func startTapperSyncer(ctx context.Context, cancel context.CancelFunc, provider
|
|||||||
MizuApiFilteringOptions: mizuApiFilteringOptions,
|
MizuApiFilteringOptions: mizuApiFilteringOptions,
|
||||||
MizuServiceAccountExists: state.mizuServiceAccountExists,
|
MizuServiceAccountExists: state.mizuServiceAccountExists,
|
||||||
ServiceMesh: config.Config.Tap.ServiceMesh,
|
ServiceMesh: config.Config.Tap.ServiceMesh,
|
||||||
|
Tls: config.Config.Tap.Tls,
|
||||||
}, startTime)
|
}, startTime)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/up9inc/mizu/cli/utils"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/cli/utils"
|
||||||
|
|
||||||
"github.com/up9inc/mizu/cli/apiserver"
|
"github.com/up9inc/mizu/cli/apiserver"
|
||||||
"github.com/up9inc/mizu/cli/config"
|
"github.com/up9inc/mizu/cli/config"
|
||||||
"github.com/up9inc/mizu/cli/mizu/fsUtils"
|
"github.com/up9inc/mizu/cli/mizu/fsUtils"
|
||||||
"github.com/up9inc/mizu/cli/mizu/version"
|
|
||||||
"github.com/up9inc/mizu/cli/uiUtils"
|
"github.com/up9inc/mizu/cli/uiUtils"
|
||||||
"github.com/up9inc/mizu/shared/kubernetes"
|
"github.com/up9inc/mizu/shared/kubernetes"
|
||||||
"github.com/up9inc/mizu/shared/logger"
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
@@ -62,14 +62,5 @@ func runMizuView() {
|
|||||||
uiUtils.OpenBrowser(url)
|
uiUtils.OpenBrowser(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isCompatible, err := version.CheckVersionCompatibility(apiServerProvider); err != nil {
|
|
||||||
logger.Log.Errorf("Failed to check versions compatibility %v", err)
|
|
||||||
cancel()
|
|
||||||
return
|
|
||||||
} else if !isCompatible {
|
|
||||||
cancel()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.WaitForFinish(ctx, cancel)
|
utils.WaitForFinish(ctx, cancel)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"`
|
||||||
@@ -38,7 +39,7 @@ type ConfigStruct struct {
|
|||||||
ConfigFilePath string `yaml:"config-path,omitempty" readonly:""`
|
ConfigFilePath string `yaml:"config-path,omitempty" readonly:""`
|
||||||
HeadlessMode bool `yaml:"headless" default:"false"`
|
HeadlessMode bool `yaml:"headless" default:"false"`
|
||||||
LogLevelStr string `yaml:"log-level,omitempty" default:"INFO" readonly:""`
|
LogLevelStr string `yaml:"log-level,omitempty" default:"INFO" readonly:""`
|
||||||
ServiceMap bool `yaml:"service-map" default:"false"`
|
ServiceMap bool `yaml:"service-map" default:"true"`
|
||||||
OAS bool `yaml:"oas,omitempty" default:"false" readonly:""`
|
OAS bool `yaml:"oas,omitempty" default:"false" readonly:""`
|
||||||
Elastic shared.ElasticConfig `yaml:"elastic"`
|
Elastic shared.ElasticConfig `yaml:"elastic"`
|
||||||
}
|
}
|
||||||
|
|||||||
9
cli/config/configStructs/checkConfig.go
Normal file
9
cli/config/configStructs/checkConfig.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package configStructs
|
||||||
|
|
||||||
|
const (
|
||||||
|
PreTapCheckName = "pre-tap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CheckConfig struct {
|
||||||
|
PreTap bool `yaml:"pre-tap"`
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ const (
|
|||||||
EnforcePolicyFile = "traffic-validation-file"
|
EnforcePolicyFile = "traffic-validation-file"
|
||||||
ContractFile = "contract"
|
ContractFile = "contract"
|
||||||
ServiceMeshName = "service-mesh"
|
ServiceMeshName = "service-mesh"
|
||||||
|
TlsName = "tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TapConfig struct {
|
type TapConfig struct {
|
||||||
@@ -45,6 +46,7 @@ type TapConfig struct {
|
|||||||
ApiServerResources shared.Resources `yaml:"api-server-resources"`
|
ApiServerResources shared.Resources `yaml:"api-server-resources"`
|
||||||
TapperResources shared.Resources `yaml:"tapper-resources"`
|
TapperResources shared.Resources `yaml:"tapper-resources"`
|
||||||
ServiceMesh bool `yaml:"service-mesh" default:"false"`
|
ServiceMesh bool `yaml:"service-mesh" default:"false"`
|
||||||
|
Tls bool `yaml:"tls" default:"false"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (config *TapConfig) PodRegex() *regexp.Regexp {
|
func (config *TapConfig) PodRegex() *regexp.Regexp {
|
||||||
|
|||||||
@@ -80,327 +80,9 @@ Notes:
|
|||||||
|
|
||||||
## List of permissions
|
## List of permissions
|
||||||
|
|
||||||
We broke down this list into few categories:
|
The permissions that are required to run Mizu depend on the configuration.
|
||||||
|
By default Mizu requires cluster-wide permissions.
|
||||||
|
If these are not available to the user, it is possible to run Mizu in namespace-restricted mode which has a reduced set of requirements.
|
||||||
|
This is done by by setting the `mizu-resources-namespace` config option. See [configuration](CONFIGURATION.md) for instructions.
|
||||||
|
|
||||||
- Required - what is needed for `mizu` to run properly on your k8s cluster
|
The different requirements are listed in [the permission templates dir](../cli/cmd/permissionFiles)
|
||||||
- Optional - permissions needed for proper name resolving for service & pod IPs
|
|
||||||
- addition required for policy validation
|
|
||||||
|
|
||||||
### Required permissions
|
|
||||||
|
|
||||||
Mizu needs following permissions on your Kubernetes cluster to run properly
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- pods
|
|
||||||
verbs:
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- services
|
|
||||||
verbs:
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- apps
|
|
||||||
resources:
|
|
||||||
- daemonsets
|
|
||||||
verbs:
|
|
||||||
- create
|
|
||||||
- patch
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- namespaces
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- services/proxy
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Permissions required running with install command or (optional) for service / pod name resolving
|
|
||||||
|
|
||||||
Mandatory permissions for running with install command.
|
|
||||||
|
|
||||||
Optional for service/pod name resolving in non install standalone
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- pods
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- services
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- apps
|
|
||||||
resources:
|
|
||||||
- daemonsets
|
|
||||||
verbs:
|
|
||||||
- create
|
|
||||||
- patch
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- namespaces
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- services/proxy
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- serviceaccounts
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- rbac.authorization.k8s.io
|
|
||||||
resources:
|
|
||||||
- clusterroles
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- rbac.authorization.k8s.io
|
|
||||||
resources:
|
|
||||||
- clusterrolebindings
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- rbac.authorization.k8s.io
|
|
||||||
resources:
|
|
||||||
- roles
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- rbac.authorization.k8s.io
|
|
||||||
resources:
|
|
||||||
- rolebindings
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- apps
|
|
||||||
- extensions
|
|
||||||
resources:
|
|
||||||
- pods
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
- apiGroups:
|
|
||||||
- apps
|
|
||||||
- extensions
|
|
||||||
resources:
|
|
||||||
- services
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
- apps
|
|
||||||
- extensions
|
|
||||||
resources:
|
|
||||||
- endpoints
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Permissions for Policy rules validation feature (opt)
|
|
||||||
|
|
||||||
Optionally, in order to use the policy rules validation feature, Mizu requires the following additional permissions:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- configmaps
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
```
|
|
||||||
|
|
||||||
- - -
|
|
||||||
|
|
||||||
#### Namespace-Restricted mode
|
|
||||||
|
|
||||||
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:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- pods
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- services
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- apps
|
|
||||||
resources:
|
|
||||||
- daemonsets
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- create
|
|
||||||
- patch
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- services/proxy
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
```
|
|
||||||
|
|
||||||
##### Name resolving in Namespace-Restricted mode (opt)
|
|
||||||
|
|
||||||
To restrict Mizu to one namespace while also resolving IPs, Mizu needs the following permissions in that namespace:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- pods
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- services
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- apps
|
|
||||||
resources:
|
|
||||||
- daemonsets
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- create
|
|
||||||
- patch
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- services/proxy
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- serviceaccounts
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- rbac.authorization.k8s.io
|
|
||||||
resources:
|
|
||||||
- roles
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- rbac.authorization.k8s.io
|
|
||||||
resources:
|
|
||||||
- rolebindings
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- create
|
|
||||||
- delete
|
|
||||||
- apiGroups:
|
|
||||||
- apps
|
|
||||||
- extensions
|
|
||||||
resources:
|
|
||||||
- pods
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
- apiGroups:
|
|
||||||
- apps
|
|
||||||
- extensions
|
|
||||||
resources:
|
|
||||||
- services
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
- apps
|
|
||||||
- extensions
|
|
||||||
resources:
|
|
||||||
- endpoints
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
# This example shows the roles required for a user to be able to use Mizu in all namespaces.
|
|
||||||
kind: ClusterRole
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
metadata:
|
|
||||||
name: mizu-runner-clusterrole
|
|
||||||
rules:
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["pods"]
|
|
||||||
verbs: ["get", "list", "watch", "delete"]
|
|
||||||
- apiGroups: [ "apps" ]
|
|
||||||
resources: [ "deployments" ]
|
|
||||||
verbs: [ "get", "create", "delete" ]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["services"]
|
|
||||||
verbs: ["get", "list", "watch", "create", "delete"]
|
|
||||||
- apiGroups: ["apps"]
|
|
||||||
resources: ["daemonsets"]
|
|
||||||
verbs: ["get", "create", "patch", "delete", "list"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["namespaces"]
|
|
||||||
verbs: ["get", "list", "watch", "create", "delete"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["services/proxy"]
|
|
||||||
verbs: ["get"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["configmaps"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["serviceaccounts"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
|
||||||
resources: ["clusterroles"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
|
||||||
resources: ["clusterrolebindings"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
|
||||||
resources: ["roles"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
|
||||||
resources: ["rolebindings"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["apps", "extensions"]
|
|
||||||
resources: ["pods"]
|
|
||||||
verbs: ["get", "list", "watch"]
|
|
||||||
- apiGroups: ["apps", "extensions"]
|
|
||||||
resources: ["services"]
|
|
||||||
verbs: ["get", "list", "watch"]
|
|
||||||
- apiGroups: ["", "apps", "extensions"]
|
|
||||||
resources: ["endpoints"]
|
|
||||||
verbs: ["get", "list", "watch"]
|
|
||||||
- apiGroups: ["events.k8s.io"]
|
|
||||||
resources: ["events"]
|
|
||||||
verbs: ["list", "watch"]
|
|
||||||
---
|
|
||||||
kind: ClusterRoleBinding
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
metadata:
|
|
||||||
name: mizu-runner-clusterrolebindings
|
|
||||||
subjects:
|
|
||||||
- kind: User
|
|
||||||
name: user1
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
roleRef:
|
|
||||||
kind: ClusterRole
|
|
||||||
name: mizu-runner-clusterrole
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
# This example shows the roles required for a user to be able to use Mizu in all namespaces.
|
|
||||||
kind: ClusterRole
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
metadata:
|
|
||||||
name: mizu-runner-clusterrole
|
|
||||||
rules:
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["pods"]
|
|
||||||
verbs: ["get", "list", "watch", "create", "delete"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["services"]
|
|
||||||
verbs: ["get", "list", "watch", "create", "delete"]
|
|
||||||
- apiGroups: ["apps"]
|
|
||||||
resources: ["daemonsets"]
|
|
||||||
verbs: ["create", "patch", "delete"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["namespaces"]
|
|
||||||
verbs: ["get", "list", "watch", "create", "delete"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["services/proxy"]
|
|
||||||
verbs: ["get"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["configmaps"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["serviceaccounts"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
|
||||||
resources: ["clusterroles"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
|
||||||
resources: ["clusterrolebindings"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
|
||||||
resources: ["roles"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
|
||||||
resources: ["rolebindings"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["apps", "extensions"]
|
|
||||||
resources: ["pods"]
|
|
||||||
verbs: ["get", "list", "watch"]
|
|
||||||
- apiGroups: ["apps", "extensions"]
|
|
||||||
resources: ["services"]
|
|
||||||
verbs: ["get", "list", "watch"]
|
|
||||||
- apiGroups: ["", "apps", "extensions"]
|
|
||||||
resources: ["endpoints"]
|
|
||||||
verbs: ["get", "list", "watch"]
|
|
||||||
- apiGroups: ["events.k8s.io"]
|
|
||||||
resources: ["events"]
|
|
||||||
verbs: ["list", "watch"]
|
|
||||||
---
|
|
||||||
kind: ClusterRoleBinding
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
metadata:
|
|
||||||
name: mizu-runner-clusterrolebindings
|
|
||||||
subjects:
|
|
||||||
- kind: User
|
|
||||||
name: user1
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
roleRef:
|
|
||||||
kind: ClusterRole
|
|
||||||
name: mizu-runner-clusterrole
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
# This example shows the roles required for a user to be able to use Mizu in a single namespace.
|
|
||||||
kind: Role
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
metadata:
|
|
||||||
name: mizu-runner-role
|
|
||||||
namespace: user1
|
|
||||||
rules:
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["pods"]
|
|
||||||
verbs: ["get", "list", "watch", "delete"]
|
|
||||||
- apiGroups: [ "apps" ]
|
|
||||||
resources: [ "deployments" ]
|
|
||||||
verbs: [ "get", "create", "delete" ]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["services"]
|
|
||||||
verbs: ["get", "list", "watch", "create", "delete"]
|
|
||||||
- apiGroups: ["apps"]
|
|
||||||
resources: ["daemonsets"]
|
|
||||||
verbs: ["get", "create", "patch", "delete", "list"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["services/proxy"]
|
|
||||||
verbs: ["get"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["configmaps"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["serviceaccounts"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
|
||||||
resources: ["roles"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
|
||||||
resources: ["rolebindings"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["apps", "extensions", ""]
|
|
||||||
resources: ["pods"]
|
|
||||||
verbs: ["get", "list", "watch"]
|
|
||||||
- apiGroups: ["apps", "extensions"]
|
|
||||||
resources: ["services"]
|
|
||||||
verbs: ["get", "list", "watch"]
|
|
||||||
- apiGroups: ["", "apps", "extensions"]
|
|
||||||
resources: ["endpoints"]
|
|
||||||
verbs: ["get", "list", "watch"]
|
|
||||||
- apiGroups: ["events.k8s.io"]
|
|
||||||
resources: ["events"]
|
|
||||||
verbs: ["list", "watch"]
|
|
||||||
---
|
|
||||||
kind: RoleBinding
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
metadata:
|
|
||||||
name: mizu-runner-rolebindings
|
|
||||||
namespace: user1
|
|
||||||
subjects:
|
|
||||||
- kind: User
|
|
||||||
name: user1
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
roleRef:
|
|
||||||
kind: Role
|
|
||||||
name: mizu-runner-role
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
# This example shows the roles required for a user to be able to use Mizu in a single namespace.
|
|
||||||
kind: Role
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
metadata:
|
|
||||||
name: mizu-runner-role
|
|
||||||
namespace: user1
|
|
||||||
rules:
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["pods"]
|
|
||||||
verbs: ["get", "list", "watch", "create", "delete"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["services"]
|
|
||||||
verbs: ["get", "list", "watch", "create", "delete"]
|
|
||||||
- apiGroups: ["apps"]
|
|
||||||
resources: ["daemonsets"]
|
|
||||||
verbs: ["get", "create", "patch", "delete"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["services/proxy"]
|
|
||||||
verbs: ["get"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["configmaps"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["serviceaccounts"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
|
||||||
resources: ["roles"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
|
||||||
resources: ["rolebindings"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["apps", "extensions"]
|
|
||||||
resources: ["pods"]
|
|
||||||
verbs: ["get", "list", "watch"]
|
|
||||||
- apiGroups: ["apps", "extensions"]
|
|
||||||
resources: ["services"]
|
|
||||||
verbs: ["get", "list", "watch"]
|
|
||||||
- apiGroups: ["", "apps", "extensions"]
|
|
||||||
resources: ["endpoints"]
|
|
||||||
verbs: ["get", "list", "watch"]
|
|
||||||
- apiGroups: ["events.k8s.io"]
|
|
||||||
resources: ["events"]
|
|
||||||
verbs: ["list", "watch"]
|
|
||||||
---
|
|
||||||
kind: RoleBinding
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
metadata:
|
|
||||||
name: mizu-runner-rolebindings
|
|
||||||
namespace: user1
|
|
||||||
subjects:
|
|
||||||
- kind: User
|
|
||||||
name: user1
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
roleRef:
|
|
||||||
kind: Role
|
|
||||||
name: mizu-runner-role
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
# This example shows the roles required for a user to be able to use Mizu in a single namespace.
|
|
||||||
kind: Role
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
metadata:
|
|
||||||
name: mizu-runner-role
|
|
||||||
namespace: user1
|
|
||||||
rules:
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["pods"]
|
|
||||||
verbs: ["get", "list", "watch", "create", "delete"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["services"]
|
|
||||||
verbs: ["get", "list", "watch", "create", "delete"]
|
|
||||||
- apiGroups: ["apps"]
|
|
||||||
resources: ["daemonsets"]
|
|
||||||
verbs: ["get", "create", "patch", "delete"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["services/proxy"]
|
|
||||||
verbs: ["get"]
|
|
||||||
- apiGroups: [ "" ]
|
|
||||||
resources: [ "configmaps" ]
|
|
||||||
verbs: [ "get", "create", "delete" ]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["serviceaccounts"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
|
||||||
resources: ["roles"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
|
||||||
resources: ["rolebindings"]
|
|
||||||
verbs: ["get", "create", "delete"]
|
|
||||||
- apiGroups: ["apps", "extensions"]
|
|
||||||
resources: ["pods"]
|
|
||||||
verbs: ["get", "list", "watch"]
|
|
||||||
- apiGroups: ["apps", "extensions"]
|
|
||||||
resources: ["services"]
|
|
||||||
verbs: ["get", "list", "watch"]
|
|
||||||
- apiGroups: ["", "apps", "extensions"]
|
|
||||||
resources: ["endpoints"]
|
|
||||||
verbs: ["get", "list", "watch"]
|
|
||||||
- apiGroups: ["events.k8s.io"]
|
|
||||||
resources: ["events"]
|
|
||||||
verbs: ["list", "watch"]
|
|
||||||
---
|
|
||||||
kind: RoleBinding
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
metadata:
|
|
||||||
name: mizu-runner-rolebindings
|
|
||||||
namespace: user1
|
|
||||||
subjects:
|
|
||||||
- kind: User
|
|
||||||
name: user1
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
roleRef:
|
|
||||||
kind: Role
|
|
||||||
name: mizu-runner-role
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
@@ -46,6 +46,7 @@ type TapperSyncerConfig struct {
|
|||||||
MizuApiFilteringOptions api.TrafficFilteringOptions
|
MizuApiFilteringOptions api.TrafficFilteringOptions
|
||||||
MizuServiceAccountExists bool
|
MizuServiceAccountExists bool
|
||||||
ServiceMesh bool
|
ServiceMesh bool
|
||||||
|
Tls bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateAndStartMizuTapperSyncer(ctx context.Context, kubernetesProvider *Provider, config TapperSyncerConfig, startTime time.Time) (*MizuTapperSyncer, error) {
|
func CreateAndStartMizuTapperSyncer(ctx context.Context, kubernetesProvider *Provider, config TapperSyncerConfig, startTime time.Time) (*MizuTapperSyncer, error) {
|
||||||
@@ -324,6 +325,7 @@ func (tapperSyncer *MizuTapperSyncer) updateMizuTappers() error {
|
|||||||
tapperSyncer.config.MizuApiFilteringOptions,
|
tapperSyncer.config.MizuApiFilteringOptions,
|
||||||
tapperSyncer.config.LogLevel,
|
tapperSyncer.config.LogLevel,
|
||||||
tapperSyncer.config.ServiceMesh,
|
tapperSyncer.config.ServiceMesh,
|
||||||
|
tapperSyncer.config.Tls,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -51,6 +52,8 @@ const (
|
|||||||
fieldManagerName = "mizu-manager"
|
fieldManagerName = "mizu-manager"
|
||||||
procfsVolumeName = "proc"
|
procfsVolumeName = "proc"
|
||||||
procfsMountPath = "/hostproc"
|
procfsMountPath = "/hostproc"
|
||||||
|
sysfsVolumeName = "sys"
|
||||||
|
sysfsMountPath = "/sys"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewProvider(kubeConfigPath string) (*Provider, error) {
|
func NewProvider(kubeConfigPath string) (*Provider, error) {
|
||||||
@@ -441,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)
|
||||||
@@ -466,11 +489,6 @@ func (provider *Provider) DoesDeploymentExist(ctx context.Context, namespace str
|
|||||||
return provider.doesResourceExist(deploymentResource, err)
|
return provider.doesResourceExist(deploymentResource, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *Provider) DoesPodExist(ctx context.Context, namespace string, name string) (bool, error) {
|
|
||||||
podResource, err := provider.clientSet.CoreV1().Pods(namespace).Get(ctx, name, metav1.GetOptions{})
|
|
||||||
return provider.doesResourceExist(podResource, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (provider *Provider) DoesServiceExist(ctx context.Context, namespace string, name string) (bool, error) {
|
func (provider *Provider) DoesServiceExist(ctx context.Context, namespace string, name string) (bool, error) {
|
||||||
serviceResource, err := provider.clientSet.CoreV1().Services(namespace).Get(ctx, name, metav1.GetOptions{})
|
serviceResource, err := provider.clientSet.CoreV1().Services(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||||
return provider.doesResourceExist(serviceResource, err)
|
return provider.doesResourceExist(serviceResource, err)
|
||||||
@@ -795,7 +813,7 @@ func (provider *Provider) CreateConfigMap(ctx context.Context, namespace string,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespace string, daemonSetName string, podImage string, tapperPodName string, apiServerPodIp string, nodeToTappedPodMap map[string][]core.Pod, serviceAccountName string, resources shared.Resources, imagePullPolicy core.PullPolicy, mizuApiFilteringOptions api.TrafficFilteringOptions, logLevel logging.Level, serviceMesh bool) error {
|
func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespace string, daemonSetName string, podImage string, tapperPodName string, apiServerPodIp string, nodeToTappedPodMap map[string][]core.Pod, serviceAccountName string, resources shared.Resources, imagePullPolicy core.PullPolicy, mizuApiFilteringOptions api.TrafficFilteringOptions, logLevel logging.Level, serviceMesh bool, tls bool) error {
|
||||||
logger.Log.Debugf("Applying %d tapper daemon sets, ns: %s, daemonSetName: %s, podImage: %s, tapperPodName: %s", len(nodeToTappedPodMap), namespace, daemonSetName, podImage, tapperPodName)
|
logger.Log.Debugf("Applying %d tapper daemon sets, ns: %s, daemonSetName: %s, podImage: %s, tapperPodName: %s", len(nodeToTappedPodMap), namespace, daemonSetName, podImage, tapperPodName)
|
||||||
|
|
||||||
if len(nodeToTappedPodMap) == 0 {
|
if len(nodeToTappedPodMap) == 0 {
|
||||||
@@ -821,7 +839,15 @@ func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespac
|
|||||||
}
|
}
|
||||||
|
|
||||||
if serviceMesh {
|
if serviceMesh {
|
||||||
mizuCmd = append(mizuCmd, "--procfs", procfsMountPath, "--servicemesh")
|
mizuCmd = append(mizuCmd, "--servicemesh")
|
||||||
|
}
|
||||||
|
|
||||||
|
if tls {
|
||||||
|
mizuCmd = append(mizuCmd, "--tls")
|
||||||
|
}
|
||||||
|
|
||||||
|
if serviceMesh || tls {
|
||||||
|
mizuCmd = append(mizuCmd, "--procfs", procfsMountPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
agentContainer := applyconfcore.Container()
|
agentContainer := applyconfcore.Container()
|
||||||
@@ -829,12 +855,21 @@ func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespac
|
|||||||
agentContainer.WithImage(podImage)
|
agentContainer.WithImage(podImage)
|
||||||
agentContainer.WithImagePullPolicy(imagePullPolicy)
|
agentContainer.WithImagePullPolicy(imagePullPolicy)
|
||||||
|
|
||||||
caps := applyconfcore.Capabilities().WithDrop("ALL").WithAdd("NET_RAW").WithAdd("NET_ADMIN")
|
caps := applyconfcore.Capabilities().WithDrop("ALL")
|
||||||
|
|
||||||
if serviceMesh {
|
caps = caps.WithAdd("NET_RAW").WithAdd("NET_ADMIN") // to listen to traffic using libpcap
|
||||||
caps = caps.WithAdd("SYS_ADMIN") // for reading /proc/PID/net/ns
|
|
||||||
caps = caps.WithAdd("SYS_PTRACE") // for setting netns to other process
|
if serviceMesh || tls {
|
||||||
caps = caps.WithAdd("DAC_OVERRIDE") // for reading /proc/PID/environ
|
caps = caps.WithAdd("SYS_ADMIN") // to read /proc/PID/net/ns + to install eBPF programs (kernel < 5.8)
|
||||||
|
caps = caps.WithAdd("SYS_PTRACE") // to set netns to other process + to open libssl.so of other process
|
||||||
|
|
||||||
|
if serviceMesh {
|
||||||
|
caps = caps.WithAdd("DAC_OVERRIDE") // to read /proc/PID/environ
|
||||||
|
}
|
||||||
|
|
||||||
|
if tls {
|
||||||
|
caps = caps.WithAdd("SYS_RESOURCE") // to change rlimits for eBPF
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
agentContainer.WithSecurityContext(applyconfcore.SecurityContext().WithCapabilities(caps))
|
agentContainer.WithSecurityContext(applyconfcore.SecurityContext().WithCapabilities(caps))
|
||||||
@@ -910,26 +945,15 @@ func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespac
|
|||||||
//
|
//
|
||||||
procfsVolume := applyconfcore.Volume()
|
procfsVolume := applyconfcore.Volume()
|
||||||
procfsVolume.WithName(procfsVolumeName).WithHostPath(applyconfcore.HostPathVolumeSource().WithPath("/proc"))
|
procfsVolume.WithName(procfsVolumeName).WithHostPath(applyconfcore.HostPathVolumeSource().WithPath("/proc"))
|
||||||
volumeMount := applyconfcore.VolumeMount().WithName(procfsVolumeName).WithMountPath(procfsMountPath).WithReadOnly(true)
|
procfsVolumeMount := applyconfcore.VolumeMount().WithName(procfsVolumeName).WithMountPath(procfsMountPath).WithReadOnly(true)
|
||||||
agentContainer.WithVolumeMounts(volumeMount)
|
agentContainer.WithVolumeMounts(procfsVolumeMount)
|
||||||
|
|
||||||
volumeName := ConfigMapName
|
// We need access to /sys in order to install certain eBPF tracepoints
|
||||||
configMapVolume := applyconfcore.VolumeApplyConfiguration{
|
//
|
||||||
Name: &volumeName,
|
sysfsVolume := applyconfcore.Volume()
|
||||||
VolumeSourceApplyConfiguration: applyconfcore.VolumeSourceApplyConfiguration{
|
sysfsVolume.WithName(sysfsVolumeName).WithHostPath(applyconfcore.HostPathVolumeSource().WithPath("/sys"))
|
||||||
ConfigMap: &applyconfcore.ConfigMapVolumeSourceApplyConfiguration{
|
sysfsVolumeMount := applyconfcore.VolumeMount().WithName(sysfsVolumeName).WithMountPath(sysfsMountPath).WithReadOnly(true)
|
||||||
LocalObjectReferenceApplyConfiguration: applyconfcore.LocalObjectReferenceApplyConfiguration{
|
agentContainer.WithVolumeMounts(sysfsVolumeMount)
|
||||||
Name: &volumeName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
mountPath := shared.ConfigDirPath
|
|
||||||
configMapVolumeMount := applyconfcore.VolumeMountApplyConfiguration{
|
|
||||||
Name: &volumeName,
|
|
||||||
MountPath: &mountPath,
|
|
||||||
}
|
|
||||||
agentContainer.WithVolumeMounts(&configMapVolumeMount)
|
|
||||||
|
|
||||||
podSpec := applyconfcore.PodSpec()
|
podSpec := applyconfcore.PodSpec()
|
||||||
podSpec.WithHostNetwork(true)
|
podSpec.WithHostNetwork(true)
|
||||||
@@ -941,7 +965,7 @@ func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespac
|
|||||||
podSpec.WithContainers(agentContainer)
|
podSpec.WithContainers(agentContainer)
|
||||||
podSpec.WithAffinity(affinity)
|
podSpec.WithAffinity(affinity)
|
||||||
podSpec.WithTolerations(noExecuteToleration, noScheduleToleration)
|
podSpec.WithTolerations(noExecuteToleration, noScheduleToleration)
|
||||||
podSpec.WithVolumes(&configMapVolume, procfsVolume)
|
podSpec.WithVolumes(procfsVolume, sysfsVolume)
|
||||||
|
|
||||||
podTemplate := applyconfcore.PodTemplateSpec()
|
podTemplate := applyconfcore.PodTemplateSpec()
|
||||||
podTemplate.WithLabels(map[string]string{
|
podTemplate.WithLabels(map[string]string{
|
||||||
@@ -954,6 +978,11 @@ func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespac
|
|||||||
labelSelector := applyconfmeta.LabelSelector()
|
labelSelector := applyconfmeta.LabelSelector()
|
||||||
labelSelector.WithMatchLabels(map[string]string{"app": tapperPodName})
|
labelSelector.WithMatchLabels(map[string]string{"app": tapperPodName})
|
||||||
|
|
||||||
|
applyOptions := metav1.ApplyOptions{
|
||||||
|
Force: true,
|
||||||
|
FieldManager: fieldManagerName,
|
||||||
|
}
|
||||||
|
|
||||||
daemonSet := applyconfapp.DaemonSet(daemonSetName, namespace)
|
daemonSet := applyconfapp.DaemonSet(daemonSetName, namespace)
|
||||||
daemonSet.
|
daemonSet.
|
||||||
WithLabels(map[string]string{
|
WithLabels(map[string]string{
|
||||||
@@ -962,7 +991,7 @@ func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespac
|
|||||||
}).
|
}).
|
||||||
WithSpec(applyconfapp.DaemonSetSpec().WithSelector(labelSelector).WithTemplate(podTemplate))
|
WithSpec(applyconfapp.DaemonSetSpec().WithSelector(labelSelector).WithTemplate(podTemplate))
|
||||||
|
|
||||||
_, err = provider.clientSet.AppsV1().DaemonSets(namespace).Apply(ctx, daemonSet, metav1.ApplyOptions{FieldManager: fieldManagerName})
|
_, err = provider.clientSet.AppsV1().DaemonSets(namespace).Apply(ctx, daemonSet, applyOptions)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1009,6 +1038,15 @@ func (provider *Provider) ListAllRunningPodsMatchingRegex(ctx context.Context, r
|
|||||||
return matchingPods, nil
|
return matchingPods, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func(provider *Provider) ListPodsByAppLabel(ctx context.Context, namespaces string, labelName string) ([]core.Pod, error) {
|
||||||
|
pods, err := provider.clientSet.CoreV1().Pods(namespaces).List(ctx, metav1.ListOptions{LabelSelector: fmt.Sprintf("app=%s", labelName)})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pods.Items, err
|
||||||
|
}
|
||||||
|
|
||||||
func (provider *Provider) ListAllNamespaces(ctx context.Context) ([]core.Namespace, error) {
|
func (provider *Provider) ListAllNamespaces(ctx context.Context) ([]core.Namespace, error) {
|
||||||
namespaces, err := provider.clientSet.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
|
namespaces, err := provider.clientSet.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -128,9 +128,14 @@ func getHttpDialer(kubernetesProvider *Provider, namespace string, podName strin
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
path := fmt.Sprintf("/api/v1/namespaces/%s/pods/%s/portforward", namespace, podName)
|
clientConfigHostUrl, err := url.Parse(kubernetesProvider.clientConfig.Host)
|
||||||
hostIP := strings.TrimLeft(kubernetesProvider.clientConfig.Host, "htps:/") // no need specify "t" twice
|
if err != nil {
|
||||||
serverURL := url.URL{Scheme: "https", Path: path, Host: hostIP}
|
return nil, fmt.Errorf("Failed parsing client config host URL %s, error %w", kubernetesProvider.clientConfig.Host, err)
|
||||||
|
}
|
||||||
|
path := fmt.Sprintf("%s/api/v1/namespaces/%s/pods/%s/portforward", clientConfigHostUrl.Path, namespace, podName)
|
||||||
|
|
||||||
|
serverURL := url.URL{Scheme: "https", Path: path, Host: clientConfigHostUrl.Host}
|
||||||
|
logger.Log.Debugf("Http dialer url %v", serverURL)
|
||||||
|
|
||||||
return spdy.NewDialer(upgrader, &http.Client{Transport: roundTripper}, http.MethodPost, &serverURL), nil
|
return spdy.NewDialer(upgrader, &http.Client{Transport: roundTripper}, http.MethodPost, &serverURL), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,11 +30,24 @@ func getMinimizedPod(fullPod core.Pod) core.Pod {
|
|||||||
Name: fullPod.Name,
|
Name: fullPod.Name,
|
||||||
},
|
},
|
||||||
Status: v1.PodStatus{
|
Status: v1.PodStatus{
|
||||||
PodIP: fullPod.Status.PodIP,
|
PodIP: fullPod.Status.PodIP,
|
||||||
|
ContainerStatuses: getMinimizedContainerStatuses(fullPod),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMinimizedContainerStatuses(fullPod core.Pod) []v1.ContainerStatus {
|
||||||
|
result := make([]v1.ContainerStatus, len(fullPod.Status.ContainerStatuses))
|
||||||
|
|
||||||
|
for i, container := range fullPod.Status.ContainerStatuses {
|
||||||
|
result[i] = v1.ContainerStatus{
|
||||||
|
ContainerID: container.ContainerID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func excludeMizuPods(pods []core.Pod) []core.Pod {
|
func excludeMizuPods(pods []core.Pod) []core.Pod {
|
||||||
mizuPrefixRegex := regexp.MustCompile("^" + MizuResourcesPrefix)
|
mizuPrefixRegex := regexp.MustCompile("^" + MizuResourcesPrefix)
|
||||||
|
|
||||||
|
|||||||
@@ -29,10 +29,21 @@ func InitLogger(logPath string) {
|
|||||||
logging.SetBackend(backend1Leveled, backend2Formatter)
|
logging.SetBackend(backend1Leveled, backend2Formatter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitLoggerStderrOnly(level logging.Level) {
|
func InitLoggerStd(level logging.Level) {
|
||||||
backend := logging.NewLogBackend(os.Stderr, "", 0)
|
var backends []logging.Backend
|
||||||
backendFormatter := logging.NewBackendFormatter(backend, format)
|
|
||||||
|
|
||||||
logging.SetBackend(backendFormatter)
|
stderrBackend := logging.NewLogBackend(os.Stderr, "", 0)
|
||||||
logging.SetLevel(level, "")
|
stderrFormater := logging.NewBackendFormatter(stderrBackend, format)
|
||||||
|
stderrLeveled := logging.AddModuleLevel(stderrFormater)
|
||||||
|
stderrLeveled.SetLevel(logging.ERROR, "")
|
||||||
|
backends = append(backends, stderrLeveled)
|
||||||
|
|
||||||
|
if level >= logging.WARNING {
|
||||||
|
stdoutBackend := logging.NewLogBackend(os.Stdout, "", 0)
|
||||||
|
stdoutFormater := logging.NewBackendFormatter(stdoutBackend, format)
|
||||||
|
stdoutLeveled := logging.AddModuleLevel(stdoutFormater)
|
||||||
|
stdoutLeveled.SetLevel(level, "")
|
||||||
|
backends = append(backends, stdoutLeveled)
|
||||||
|
}
|
||||||
|
logging.SetBackend(backends...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ type WebSocketMessageType string
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
WebSocketMessageTypeEntry WebSocketMessageType = "entry"
|
WebSocketMessageTypeEntry WebSocketMessageType = "entry"
|
||||||
|
WebSocketMessageTypeFullEntry WebSocketMessageType = "fullEntry"
|
||||||
WebSocketMessageTypeTappedEntry WebSocketMessageType = "tappedEntry"
|
WebSocketMessageTypeTappedEntry WebSocketMessageType = "tappedEntry"
|
||||||
WebSocketMessageTypeUpdateStatus WebSocketMessageType = "status"
|
WebSocketMessageTypeUpdateStatus WebSocketMessageType = "status"
|
||||||
WebSocketMessageTypeAnalyzeStatus WebSocketMessageType = "analyzeStatus"
|
WebSocketMessageTypeAnalyzeStatus WebSocketMessageType = "analyzeStatus"
|
||||||
@@ -43,6 +44,7 @@ type MizuAgentConfig struct {
|
|||||||
StandaloneMode bool `json:"standaloneMode"`
|
StandaloneMode bool `json:"standaloneMode"`
|
||||||
ServiceMap bool `json:"serviceMap"`
|
ServiceMap bool `json:"serviceMap"`
|
||||||
OAS bool `json:"oas"`
|
OAS bool `json:"oas"`
|
||||||
|
Telemetry bool `json:"telemetry"`
|
||||||
Elastic ElasticConfig `json:"elastic"`
|
Elastic ElasticConfig `json:"elastic"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,10 +39,9 @@ type TCP struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Extension struct {
|
type Extension struct {
|
||||||
Protocol *Protocol
|
Protocol *Protocol
|
||||||
Path string
|
Path string
|
||||||
Dissector Dissector
|
Dissector Dissector
|
||||||
MatcherMap *sync.Map
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConnectionInfo struct {
|
type ConnectionInfo struct {
|
||||||
@@ -62,7 +61,6 @@ type TcpID struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CounterPair struct {
|
type CounterPair struct {
|
||||||
StreamId int64
|
|
||||||
Request uint
|
Request uint
|
||||||
Response uint
|
Response uint
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
@@ -100,10 +98,16 @@ type SuperIdentifier struct {
|
|||||||
type Dissector interface {
|
type Dissector interface {
|
||||||
Register(*Extension)
|
Register(*Extension)
|
||||||
Ping()
|
Ping()
|
||||||
Dissect(b *bufio.Reader, isClient bool, tcpID *TcpID, counterPair *CounterPair, superTimer *SuperTimer, superIdentifier *SuperIdentifier, emitter Emitter, options *TrafficFilteringOptions) error
|
Dissect(b *bufio.Reader, isClient bool, tcpID *TcpID, counterPair *CounterPair, superTimer *SuperTimer, superIdentifier *SuperIdentifier, emitter Emitter, options *TrafficFilteringOptions, reqResMatcher RequestResponseMatcher) error
|
||||||
Analyze(item *OutputChannelItem, resolvedSource string, resolvedDestination string) *Entry
|
Analyze(item *OutputChannelItem, resolvedSource string, resolvedDestination string, namespace string) *Entry
|
||||||
Represent(request map[string]interface{}, response map[string]interface{}) (object []byte, bodySize int64, err error)
|
Represent(request map[string]interface{}, response map[string]interface{}) (object []byte, bodySize int64, err error)
|
||||||
Macros() map[string]string
|
Macros() map[string]string
|
||||||
|
NewResponseRequestMatcher() RequestResponseMatcher
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequestResponseMatcher interface {
|
||||||
|
GetMap() *sync.Map
|
||||||
|
SetMaxTry(value int)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Emitting struct {
|
type Emitting struct {
|
||||||
@@ -125,6 +129,7 @@ type Entry struct {
|
|||||||
Protocol Protocol `json:"proto"`
|
Protocol Protocol `json:"proto"`
|
||||||
Source *TCP `json:"src"`
|
Source *TCP `json:"src"`
|
||||||
Destination *TCP `json:"dst"`
|
Destination *TCP `json:"dst"`
|
||||||
|
Namespace string `json:"namespace,omitempty"`
|
||||||
Outgoing bool `json:"outgoing"`
|
Outgoing bool `json:"outgoing"`
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
StartTime time.Time `json:"startTime"`
|
StartTime time.Time `json:"startTime"`
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ type Cleaner struct {
|
|||||||
connectionTimeout time.Duration
|
connectionTimeout time.Duration
|
||||||
stats CleanerStats
|
stats CleanerStats
|
||||||
statsMutex sync.Mutex
|
statsMutex sync.Mutex
|
||||||
|
streamsMap *tcpStreamMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cl *Cleaner) clean() {
|
func (cl *Cleaner) clean() {
|
||||||
@@ -32,10 +33,15 @@ func (cl *Cleaner) clean() {
|
|||||||
flushed, closed := cl.assembler.FlushCloseOlderThan(startCleanTime.Add(-cl.connectionTimeout))
|
flushed, closed := cl.assembler.FlushCloseOlderThan(startCleanTime.Add(-cl.connectionTimeout))
|
||||||
cl.assemblerMutex.Unlock()
|
cl.assemblerMutex.Unlock()
|
||||||
|
|
||||||
for _, extension := range extensions {
|
cl.streamsMap.streams.Range(func(k, v interface{}) bool {
|
||||||
deleted := deleteOlderThan(extension.MatcherMap, startCleanTime.Add(-cl.connectionTimeout))
|
reqResMatcher := v.(*tcpStreamWrapper).reqResMatcher
|
||||||
|
if reqResMatcher == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
deleted := deleteOlderThan(reqResMatcher.GetMap(), startCleanTime.Add(-cl.connectionTimeout))
|
||||||
cl.stats.deleted += deleted
|
cl.stats.deleted += deleted
|
||||||
}
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
cl.statsMutex.Lock()
|
cl.statsMutex.Lock()
|
||||||
logger.Log.Debugf("Assembler Stats after cleaning %s", cl.assembler.Dump())
|
logger.Log.Debugf("Assembler Stats after cleaning %s", cl.assembler.Dump())
|
||||||
|
|||||||
16
tap/extensions/amqp/Makefile
Normal file
16
tap/extensions/amqp/Makefile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
skipbin := $$(find bin -mindepth 1 -maxdepth 1)
|
||||||
|
skipexpect := $$(find expect -mindepth 1 -maxdepth 1)
|
||||||
|
|
||||||
|
test: test-pull-bin test-pull-expect
|
||||||
|
@MIZU_TEST=1 go test -v ./... -coverpkg=./... -race -coverprofile=coverage.out -covermode=atomic
|
||||||
|
|
||||||
|
test-update: test-pull-bin
|
||||||
|
@MIZU_TEST=1 TEST_UPDATE=1 go test -v ./... -coverpkg=./... -coverprofile=coverage.out -covermode=atomic
|
||||||
|
|
||||||
|
test-pull-bin:
|
||||||
|
@mkdir -p bin
|
||||||
|
@[ "${skipbin}" ] && echo "Skipping downloading BINs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp gs://static.up9.io/mizu/test-pcap/bin/amqp/\*.bin bin
|
||||||
|
|
||||||
|
test-pull-expect:
|
||||||
|
@mkdir -p expect
|
||||||
|
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect/amqp/\* expect
|
||||||
@@ -2,8 +2,16 @@ module github.com/up9inc/mizu/tap/extensions/amqp
|
|||||||
|
|
||||||
go 1.17
|
go 1.17
|
||||||
|
|
||||||
require github.com/up9inc/mizu/tap/api v0.0.0
|
require (
|
||||||
|
github.com/stretchr/testify v1.7.0
|
||||||
|
github.com/up9inc/mizu/tap/api v0.0.0
|
||||||
|
)
|
||||||
|
|
||||||
require github.com/google/martian v2.1.0+incompatible // indirect
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.0 // indirect
|
||||||
|
github.com/google/martian v2.1.0+incompatible // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||||
|
)
|
||||||
|
|
||||||
replace github.com/up9inc/mizu/tap/api v0.0.0 => ../../api
|
replace github.com/up9inc/mizu/tap/api v0.0.0 => ../../api
|
||||||
|
|||||||
@@ -1,2 +1,13 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package amqp
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -282,6 +283,9 @@ func representBasicPublish(event map[string]interface{}) []interface{} {
|
|||||||
Selector: fmt.Sprintf(`request.properties.headers["%s"]`, name),
|
Selector: fmt.Sprintf(`request.properties.headers["%s"]`, name),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
sort.Slice(headers, func(i, j int) bool {
|
||||||
|
return headers[i].Name < headers[j].Name
|
||||||
|
})
|
||||||
headersMarshaled, _ := json.Marshal(headers)
|
headersMarshaled, _ := json.Marshal(headers)
|
||||||
rep = append(rep, api.SectionData{
|
rep = append(rep, api.SectionData{
|
||||||
Type: api.TABLE,
|
Type: api.TABLE,
|
||||||
@@ -366,6 +370,9 @@ func representBasicDeliver(event map[string]interface{}) []interface{} {
|
|||||||
Selector: fmt.Sprintf(`request.properties.headers["%s"]`, name),
|
Selector: fmt.Sprintf(`request.properties.headers["%s"]`, name),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
sort.Slice(headers, func(i, j int) bool {
|
||||||
|
return headers[i].Name < headers[j].Name
|
||||||
|
})
|
||||||
headersMarshaled, _ := json.Marshal(headers)
|
headersMarshaled, _ := json.Marshal(headers)
|
||||||
rep = append(rep, api.SectionData{
|
rep = append(rep, api.SectionData{
|
||||||
Type: api.TABLE,
|
Type: api.TABLE,
|
||||||
@@ -438,6 +445,9 @@ func representQueueDeclare(event map[string]interface{}) []interface{} {
|
|||||||
Selector: fmt.Sprintf(`request.arguments["%s"]`, name),
|
Selector: fmt.Sprintf(`request.arguments["%s"]`, name),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
sort.Slice(headers, func(i, j int) bool {
|
||||||
|
return headers[i].Name < headers[j].Name
|
||||||
|
})
|
||||||
headersMarshaled, _ := json.Marshal(headers)
|
headersMarshaled, _ := json.Marshal(headers)
|
||||||
rep = append(rep, api.SectionData{
|
rep = append(rep, api.SectionData{
|
||||||
Type: api.TABLE,
|
Type: api.TABLE,
|
||||||
@@ -504,6 +514,9 @@ func representExchangeDeclare(event map[string]interface{}) []interface{} {
|
|||||||
Selector: fmt.Sprintf(`request.arguments["%s"]`, name),
|
Selector: fmt.Sprintf(`request.arguments["%s"]`, name),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
sort.Slice(headers, func(i, j int) bool {
|
||||||
|
return headers[i].Name < headers[j].Name
|
||||||
|
})
|
||||||
headersMarshaled, _ := json.Marshal(headers)
|
headersMarshaled, _ := json.Marshal(headers)
|
||||||
rep = append(rep, api.SectionData{
|
rep = append(rep, api.SectionData{
|
||||||
Type: api.TABLE,
|
Type: api.TABLE,
|
||||||
@@ -565,6 +578,9 @@ func representConnectionStart(event map[string]interface{}) []interface{} {
|
|||||||
Selector: fmt.Sprintf(`request.serverProperties["%s"]`, name),
|
Selector: fmt.Sprintf(`request.serverProperties["%s"]`, name),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
sort.Slice(headers, func(i, j int) bool {
|
||||||
|
return headers[i].Name < headers[j].Name
|
||||||
|
})
|
||||||
headersMarshaled, _ := json.Marshal(headers)
|
headersMarshaled, _ := json.Marshal(headers)
|
||||||
rep = append(rep, api.SectionData{
|
rep = append(rep, api.SectionData{
|
||||||
Type: api.TABLE,
|
Type: api.TABLE,
|
||||||
@@ -656,6 +672,9 @@ func representQueueBind(event map[string]interface{}) []interface{} {
|
|||||||
Selector: fmt.Sprintf(`request.arguments["%s"]`, name),
|
Selector: fmt.Sprintf(`request.arguments["%s"]`, name),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
sort.Slice(headers, func(i, j int) bool {
|
||||||
|
return headers[i].Name < headers[j].Name
|
||||||
|
})
|
||||||
headersMarshaled, _ := json.Marshal(headers)
|
headersMarshaled, _ := json.Marshal(headers)
|
||||||
rep = append(rep, api.SectionData{
|
rep = append(rep, api.SectionData{
|
||||||
Type: api.TABLE,
|
Type: api.TABLE,
|
||||||
@@ -717,6 +736,9 @@ func representBasicConsume(event map[string]interface{}) []interface{} {
|
|||||||
Selector: fmt.Sprintf(`request.arguments["%s"]`, name),
|
Selector: fmt.Sprintf(`request.arguments["%s"]`, name),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
sort.Slice(headers, func(i, j int) bool {
|
||||||
|
return headers[i].Name < headers[j].Name
|
||||||
|
})
|
||||||
headersMarshaled, _ := json.Marshal(headers)
|
headersMarshaled, _ := json.Marshal(headers)
|
||||||
rep = append(rep, api.SectionData{
|
rep = append(rep, api.SectionData{
|
||||||
Type: api.TABLE,
|
Type: api.TABLE,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/up9inc/mizu/tap/api"
|
"github.com/up9inc/mizu/tap/api"
|
||||||
)
|
)
|
||||||
@@ -26,10 +27,6 @@ var protocol api.Protocol = api.Protocol{
|
|||||||
Priority: 1,
|
Priority: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
|
||||||
log.Println("Initializing AMQP extension...")
|
|
||||||
}
|
|
||||||
|
|
||||||
type dissecting string
|
type dissecting string
|
||||||
|
|
||||||
func (d dissecting) Register(extension *api.Extension) {
|
func (d dissecting) Register(extension *api.Extension) {
|
||||||
@@ -42,7 +39,7 @@ func (d dissecting) Ping() {
|
|||||||
|
|
||||||
const amqpRequest string = "amqp_request"
|
const amqpRequest string = "amqp_request"
|
||||||
|
|
||||||
func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, superIdentifier *api.SuperIdentifier, emitter api.Emitter, options *api.TrafficFilteringOptions) error {
|
func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, superIdentifier *api.SuperIdentifier, emitter api.Emitter, options *api.TrafficFilteringOptions, _reqResMatcher api.RequestResponseMatcher) error {
|
||||||
r := AmqpReader{b}
|
r := AmqpReader{b}
|
||||||
|
|
||||||
var remaining int
|
var remaining int
|
||||||
@@ -85,7 +82,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
|
|||||||
frame, err := r.ReadFrame()
|
frame, err := r.ReadFrame()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
// We must read until we see an EOF... very important!
|
// We must read until we see an EOF... very important!
|
||||||
return errors.New("AMQP EOF")
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch f := frame.(type) {
|
switch f := frame.(type) {
|
||||||
@@ -96,6 +93,12 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
|
|||||||
// start content state
|
// start content state
|
||||||
header = f
|
header = f
|
||||||
remaining = int(header.Size)
|
remaining = int(header.Size)
|
||||||
|
|
||||||
|
// Workaround for `Time.MarshalJSON: year outside of range [0,9999]` error
|
||||||
|
if header.Properties.Timestamp.Year() > 9999 {
|
||||||
|
header.Properties.Timestamp = time.Time{}.UTC()
|
||||||
|
}
|
||||||
|
|
||||||
switch lastMethodFrameMessage.(type) {
|
switch lastMethodFrameMessage.(type) {
|
||||||
case *BasicPublish:
|
case *BasicPublish:
|
||||||
eventBasicPublish.Properties = header.Properties
|
eventBasicPublish.Properties = header.Properties
|
||||||
@@ -212,7 +215,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string, resolvedDestination string) *api.Entry {
|
func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string, resolvedDestination string, namespace string) *api.Entry {
|
||||||
request := item.Pair.Request.Payload.(map[string]interface{})
|
request := item.Pair.Request.Payload.(map[string]interface{})
|
||||||
reqDetails := request["details"].(map[string]interface{})
|
reqDetails := request["details"].(map[string]interface{})
|
||||||
|
|
||||||
@@ -254,6 +257,7 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
|
|||||||
IP: item.ConnectionInfo.ServerIP,
|
IP: item.ConnectionInfo.ServerIP,
|
||||||
Port: item.ConnectionInfo.ServerPort,
|
Port: item.ConnectionInfo.ServerPort,
|
||||||
},
|
},
|
||||||
|
Namespace: namespace,
|
||||||
Outgoing: item.ConnectionInfo.IsOutgoing,
|
Outgoing: item.ConnectionInfo.IsOutgoing,
|
||||||
Request: reqDetails,
|
Request: reqDetails,
|
||||||
Method: request["method"].(string),
|
Method: request["method"].(string),
|
||||||
@@ -300,6 +304,10 @@ func (d dissecting) Macros() map[string]string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d dissecting) NewResponseRequestMatcher() api.RequestResponseMatcher {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var Dissector dissecting
|
var Dissector dissecting
|
||||||
|
|
||||||
func NewDissector() api.Dissector {
|
func NewDissector() api.Dissector {
|
||||||
|
|||||||
289
tap/extensions/amqp/main_test.go
Normal file
289
tap/extensions/amqp/main_test.go
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
package amqp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/up9inc/mizu/tap/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
binDir = "bin"
|
||||||
|
patternBin = "*_req.bin"
|
||||||
|
patternDissect = "*.json"
|
||||||
|
msgDissecting = "Dissecting:"
|
||||||
|
msgAnalyzing = "Analyzing:"
|
||||||
|
msgRepresenting = "Representing:"
|
||||||
|
respSuffix = "_res.bin"
|
||||||
|
expectDir = "expect"
|
||||||
|
dissectDir = "dissect"
|
||||||
|
analyzeDir = "analyze"
|
||||||
|
representDir = "represent"
|
||||||
|
testUpdate = "TEST_UPDATE"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRegister(t *testing.T) {
|
||||||
|
dissector := NewDissector()
|
||||||
|
extension := &api.Extension{}
|
||||||
|
dissector.Register(extension)
|
||||||
|
assert.Equal(t, "amqp", extension.Protocol.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMacros(t *testing.T) {
|
||||||
|
expectedMacros := map[string]string{
|
||||||
|
"amqp": `proto.name == "amqp"`,
|
||||||
|
}
|
||||||
|
dissector := NewDissector()
|
||||||
|
macros := dissector.Macros()
|
||||||
|
assert.Equal(t, expectedMacros, macros)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPing(t *testing.T) {
|
||||||
|
dissector := NewDissector()
|
||||||
|
dissector.Ping()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDissect(t *testing.T) {
|
||||||
|
_, testUpdateEnabled := os.LookupEnv(testUpdate)
|
||||||
|
|
||||||
|
expectDirDissect := path.Join(expectDir, dissectDir)
|
||||||
|
|
||||||
|
if testUpdateEnabled {
|
||||||
|
os.RemoveAll(expectDirDissect)
|
||||||
|
err := os.MkdirAll(expectDirDissect, 0775)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dissector := NewDissector()
|
||||||
|
paths, err := filepath.Glob(path.Join(binDir, patternBin))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
options := &api.TrafficFilteringOptions{
|
||||||
|
IgnoredUserAgents: []string{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, _path := range paths {
|
||||||
|
basePath := _path[:len(_path)-8]
|
||||||
|
|
||||||
|
// Channel to verify the output
|
||||||
|
itemChannel := make(chan *api.OutputChannelItem)
|
||||||
|
var emitter api.Emitter = &api.Emitting{
|
||||||
|
AppStats: &api.AppStats{},
|
||||||
|
OutputChannel: itemChannel,
|
||||||
|
}
|
||||||
|
|
||||||
|
var items []*api.OutputChannelItem
|
||||||
|
stop := make(chan bool)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stop:
|
||||||
|
return
|
||||||
|
case item := <-itemChannel:
|
||||||
|
items = append(items, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Stream level
|
||||||
|
counterPair := &api.CounterPair{
|
||||||
|
Request: 0,
|
||||||
|
Response: 0,
|
||||||
|
}
|
||||||
|
superIdentifier := &api.SuperIdentifier{}
|
||||||
|
|
||||||
|
// Request
|
||||||
|
pathClient := _path
|
||||||
|
fmt.Printf("%s %s\n", msgDissecting, pathClient)
|
||||||
|
fileClient, err := os.Open(pathClient)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
bufferClient := bufio.NewReader(fileClient)
|
||||||
|
tcpIDClient := &api.TcpID{
|
||||||
|
SrcIP: "1",
|
||||||
|
DstIP: "2",
|
||||||
|
SrcPort: "1",
|
||||||
|
DstPort: "2",
|
||||||
|
}
|
||||||
|
reqResMatcher := dissector.NewResponseRequestMatcher()
|
||||||
|
err = dissector.Dissect(bufferClient, true, tcpIDClient, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher)
|
||||||
|
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response
|
||||||
|
pathServer := basePath + respSuffix
|
||||||
|
fmt.Printf("%s %s\n", msgDissecting, pathServer)
|
||||||
|
fileServer, err := os.Open(pathServer)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
bufferServer := bufio.NewReader(fileServer)
|
||||||
|
tcpIDServer := &api.TcpID{
|
||||||
|
SrcIP: "2",
|
||||||
|
DstIP: "1",
|
||||||
|
SrcPort: "2",
|
||||||
|
DstPort: "1",
|
||||||
|
}
|
||||||
|
err = dissector.Dissect(bufferServer, false, tcpIDServer, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher)
|
||||||
|
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileClient.Close()
|
||||||
|
fileServer.Close()
|
||||||
|
|
||||||
|
pathExpect := path.Join(expectDirDissect, fmt.Sprintf("%s.json", basePath[4:]))
|
||||||
|
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
|
||||||
|
stop <- true
|
||||||
|
|
||||||
|
marshaled, err := json.Marshal(items)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
if testUpdateEnabled {
|
||||||
|
if len(items) > 0 {
|
||||||
|
err = os.WriteFile(pathExpect, marshaled, 0644)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err := os.Stat(pathExpect); errors.Is(err, os.ErrNotExist) {
|
||||||
|
assert.Len(t, items, 0)
|
||||||
|
} else {
|
||||||
|
expectedBytes, err := ioutil.ReadFile(pathExpect)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.JSONEq(t, string(expectedBytes), string(marshaled))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnalyze(t *testing.T) {
|
||||||
|
_, testUpdateEnabled := os.LookupEnv(testUpdate)
|
||||||
|
|
||||||
|
expectDirDissect := path.Join(expectDir, dissectDir)
|
||||||
|
expectDirAnalyze := path.Join(expectDir, analyzeDir)
|
||||||
|
|
||||||
|
if testUpdateEnabled {
|
||||||
|
os.RemoveAll(expectDirAnalyze)
|
||||||
|
err := os.MkdirAll(expectDirAnalyze, 0775)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dissector := NewDissector()
|
||||||
|
paths, err := filepath.Glob(path.Join(expectDirDissect, patternDissect))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, _path := range paths {
|
||||||
|
fmt.Printf("%s %s\n", msgAnalyzing, _path)
|
||||||
|
|
||||||
|
bytes, err := ioutil.ReadFile(_path)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
var items []*api.OutputChannelItem
|
||||||
|
err = json.Unmarshal(bytes, &items)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
var entries []*api.Entry
|
||||||
|
for _, item := range items {
|
||||||
|
entry := dissector.Analyze(item, "", "", "")
|
||||||
|
entries = append(entries, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
pathExpect := path.Join(expectDirAnalyze, filepath.Base(_path))
|
||||||
|
|
||||||
|
marshaled, err := json.Marshal(entries)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
if testUpdateEnabled {
|
||||||
|
if len(entries) > 0 {
|
||||||
|
err = os.WriteFile(pathExpect, marshaled, 0644)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err := os.Stat(pathExpect); errors.Is(err, os.ErrNotExist) {
|
||||||
|
assert.Len(t, items, 0)
|
||||||
|
} else {
|
||||||
|
expectedBytes, err := ioutil.ReadFile(pathExpect)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.JSONEq(t, string(expectedBytes), string(marshaled))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRepresent(t *testing.T) {
|
||||||
|
_, testUpdateEnabled := os.LookupEnv(testUpdate)
|
||||||
|
|
||||||
|
expectDirAnalyze := path.Join(expectDir, analyzeDir)
|
||||||
|
expectDirRepresent := path.Join(expectDir, representDir)
|
||||||
|
|
||||||
|
if testUpdateEnabled {
|
||||||
|
os.RemoveAll(expectDirRepresent)
|
||||||
|
err := os.MkdirAll(expectDirRepresent, 0775)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dissector := NewDissector()
|
||||||
|
paths, err := filepath.Glob(path.Join(expectDirAnalyze, patternDissect))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, _path := range paths {
|
||||||
|
fmt.Printf("%s %s\n", msgRepresenting, _path)
|
||||||
|
|
||||||
|
bytes, err := ioutil.ReadFile(_path)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
var entries []*api.Entry
|
||||||
|
err = json.Unmarshal(bytes, &entries)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
var objects []string
|
||||||
|
for _, entry := range entries {
|
||||||
|
object, _, err := dissector.Represent(entry.Request, entry.Response)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
objects = append(objects, string(object))
|
||||||
|
}
|
||||||
|
|
||||||
|
pathExpect := path.Join(expectDirRepresent, filepath.Base(_path))
|
||||||
|
|
||||||
|
marshaled, err := json.Marshal(objects)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
if testUpdateEnabled {
|
||||||
|
if len(objects) > 0 {
|
||||||
|
err = os.WriteFile(pathExpect, marshaled, 0644)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err := os.Stat(pathExpect); errors.Is(err, os.ErrNotExist) {
|
||||||
|
assert.Len(t, objects, 0)
|
||||||
|
} else {
|
||||||
|
expectedBytes, err := ioutil.ReadFile(pathExpect)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.JSONEq(t, string(expectedBytes), string(marshaled))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -140,7 +140,7 @@ func readTimestamp(r io.Reader) (v time.Time, err error) {
|
|||||||
if err = binary.Read(r, binary.BigEndian, &sec); err != nil {
|
if err = binary.Read(r, binary.BigEndian, &sec); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return time.Unix(sec, 0), nil
|
return time.Unix(sec, 0).UTC(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -223,41 +223,8 @@ type Decimal struct {
|
|||||||
//
|
//
|
||||||
type Table map[string]interface{}
|
type Table map[string]interface{}
|
||||||
|
|
||||||
func validateField(f interface{}) error {
|
|
||||||
switch fv := f.(type) {
|
|
||||||
case nil, bool, byte, int, int16, int32, int64, float32, float64, string, []byte, Decimal, time.Time:
|
|
||||||
return nil
|
|
||||||
|
|
||||||
case []interface{}:
|
|
||||||
for _, v := range fv {
|
|
||||||
if err := validateField(v); err != nil {
|
|
||||||
return fmt.Errorf("in array %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
|
|
||||||
case Table:
|
|
||||||
for k, v := range fv {
|
|
||||||
if err := validateField(v); err != nil {
|
|
||||||
return fmt.Errorf("table field %q %s", k, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("value %T not supported", f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate returns and error if any Go types in the table are incompatible with AMQP types.
|
|
||||||
func (t Table) Validate() error {
|
|
||||||
return validateField(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Message interface {
|
type Message interface {
|
||||||
id() (uint16, uint16)
|
|
||||||
wait() bool
|
|
||||||
read(io.Reader) error
|
read(io.Reader) error
|
||||||
write(io.Writer) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -286,8 +253,6 @@ system calls to read a frame.
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
type frame interface {
|
type frame interface {
|
||||||
write(io.Writer) error
|
|
||||||
channel() uint16
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type AmqpReader struct {
|
type AmqpReader struct {
|
||||||
@@ -323,8 +288,6 @@ type MethodFrame struct {
|
|||||||
Method Message
|
Method Message
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *MethodFrame) channel() uint16 { return f.ChannelId }
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Heartbeating is a technique designed to undo one of TCP/IP's features, namely
|
Heartbeating is a technique designed to undo one of TCP/IP's features, namely
|
||||||
its ability to recover from a broken physical connection by closing only after
|
its ability to recover from a broken physical connection by closing only after
|
||||||
@@ -338,8 +301,6 @@ type HeartbeatFrame struct {
|
|||||||
ChannelId uint16
|
ChannelId uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *HeartbeatFrame) channel() uint16 { return f.ChannelId }
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Certain methods (such as Basic.Publish, Basic.Deliver, etc.) are formally
|
Certain methods (such as Basic.Publish, Basic.Deliver, etc.) are formally
|
||||||
defined as carrying content. When a peer sends such a method frame, it always
|
defined as carrying content. When a peer sends such a method frame, it always
|
||||||
@@ -367,8 +328,6 @@ type HeaderFrame struct {
|
|||||||
Properties Properties
|
Properties Properties
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *HeaderFrame) channel() uint16 { return f.ChannelId }
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Content is the application data we carry from client-to-client via the AMQP
|
Content is the application data we carry from client-to-client via the AMQP
|
||||||
server. Content is, roughly speaking, a set of properties plus a binary data
|
server. Content is, roughly speaking, a set of properties plus a binary data
|
||||||
@@ -388,5 +347,3 @@ type BodyFrame struct {
|
|||||||
ChannelId uint16
|
ChannelId uint16
|
||||||
Body []byte
|
Body []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *BodyFrame) channel() uint16 { return f.ChannelId }
|
|
||||||
|
|||||||
@@ -1,403 +0,0 @@
|
|||||||
// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
// Source code and contact info at http://github.com/streadway/amqp
|
|
||||||
|
|
||||||
package amqp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"math"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (f *MethodFrame) write(w io.Writer) (err error) {
|
|
||||||
var payload bytes.Buffer
|
|
||||||
|
|
||||||
if f.Method == nil {
|
|
||||||
return errors.New("malformed frame: missing method")
|
|
||||||
}
|
|
||||||
|
|
||||||
class, method := f.Method.id()
|
|
||||||
|
|
||||||
if err = binary.Write(&payload, binary.BigEndian, class); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = binary.Write(&payload, binary.BigEndian, method); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = f.Method.write(&payload); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return writeFrame(w, frameMethod, f.ChannelId, payload.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Heartbeat
|
|
||||||
//
|
|
||||||
// Payload is empty
|
|
||||||
func (f *HeartbeatFrame) write(w io.Writer) (err error) {
|
|
||||||
return writeFrame(w, frameHeartbeat, f.ChannelId, []byte{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// CONTENT HEADER
|
|
||||||
// 0 2 4 12 14
|
|
||||||
// +----------+--------+-----------+----------------+------------- - -
|
|
||||||
// | class-id | weight | body size | property flags | property list...
|
|
||||||
// +----------+--------+-----------+----------------+------------- - -
|
|
||||||
// short short long long short remainder...
|
|
||||||
//
|
|
||||||
func (f *HeaderFrame) write(w io.Writer) (err error) {
|
|
||||||
var payload bytes.Buffer
|
|
||||||
var zeroTime time.Time
|
|
||||||
|
|
||||||
if err = binary.Write(&payload, binary.BigEndian, f.ClassId); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = binary.Write(&payload, binary.BigEndian, f.weight); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = binary.Write(&payload, binary.BigEndian, f.Size); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// First pass will build the mask to be serialized, second pass will serialize
|
|
||||||
// each of the fields that appear in the mask.
|
|
||||||
|
|
||||||
var mask uint16
|
|
||||||
|
|
||||||
if len(f.Properties.ContentType) > 0 {
|
|
||||||
mask = mask | flagContentType
|
|
||||||
}
|
|
||||||
if len(f.Properties.ContentEncoding) > 0 {
|
|
||||||
mask = mask | flagContentEncoding
|
|
||||||
}
|
|
||||||
if f.Properties.Headers != nil && len(f.Properties.Headers) > 0 {
|
|
||||||
mask = mask | flagHeaders
|
|
||||||
}
|
|
||||||
if f.Properties.DeliveryMode > 0 {
|
|
||||||
mask = mask | flagDeliveryMode
|
|
||||||
}
|
|
||||||
if f.Properties.Priority > 0 {
|
|
||||||
mask = mask | flagPriority
|
|
||||||
}
|
|
||||||
if len(f.Properties.CorrelationId) > 0 {
|
|
||||||
mask = mask | flagCorrelationId
|
|
||||||
}
|
|
||||||
if len(f.Properties.ReplyTo) > 0 {
|
|
||||||
mask = mask | flagReplyTo
|
|
||||||
}
|
|
||||||
if len(f.Properties.Expiration) > 0 {
|
|
||||||
mask = mask | flagExpiration
|
|
||||||
}
|
|
||||||
if len(f.Properties.MessageId) > 0 {
|
|
||||||
mask = mask | flagMessageId
|
|
||||||
}
|
|
||||||
if f.Properties.Timestamp != zeroTime {
|
|
||||||
mask = mask | flagTimestamp
|
|
||||||
}
|
|
||||||
if len(f.Properties.Type) > 0 {
|
|
||||||
mask = mask | flagType
|
|
||||||
}
|
|
||||||
if len(f.Properties.UserId) > 0 {
|
|
||||||
mask = mask | flagUserId
|
|
||||||
}
|
|
||||||
if len(f.Properties.AppId) > 0 {
|
|
||||||
mask = mask | flagAppId
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = binary.Write(&payload, binary.BigEndian, mask); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if hasProperty(mask, flagContentType) {
|
|
||||||
if err = writeShortstr(&payload, f.Properties.ContentType); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hasProperty(mask, flagContentEncoding) {
|
|
||||||
if err = writeShortstr(&payload, f.Properties.ContentEncoding); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hasProperty(mask, flagHeaders) {
|
|
||||||
if err = writeTable(&payload, f.Properties.Headers); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hasProperty(mask, flagDeliveryMode) {
|
|
||||||
if err = binary.Write(&payload, binary.BigEndian, f.Properties.DeliveryMode); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hasProperty(mask, flagPriority) {
|
|
||||||
if err = binary.Write(&payload, binary.BigEndian, f.Properties.Priority); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hasProperty(mask, flagCorrelationId) {
|
|
||||||
if err = writeShortstr(&payload, f.Properties.CorrelationId); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hasProperty(mask, flagReplyTo) {
|
|
||||||
if err = writeShortstr(&payload, f.Properties.ReplyTo); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hasProperty(mask, flagExpiration) {
|
|
||||||
if err = writeShortstr(&payload, f.Properties.Expiration); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hasProperty(mask, flagMessageId) {
|
|
||||||
if err = writeShortstr(&payload, f.Properties.MessageId); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hasProperty(mask, flagTimestamp) {
|
|
||||||
if err = binary.Write(&payload, binary.BigEndian, uint64(f.Properties.Timestamp.Unix())); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hasProperty(mask, flagType) {
|
|
||||||
if err = writeShortstr(&payload, f.Properties.Type); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hasProperty(mask, flagUserId) {
|
|
||||||
if err = writeShortstr(&payload, f.Properties.UserId); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hasProperty(mask, flagAppId) {
|
|
||||||
if err = writeShortstr(&payload, f.Properties.AppId); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return writeFrame(w, frameHeader, f.ChannelId, payload.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Body
|
|
||||||
//
|
|
||||||
// Payload is one byterange from the full body who's size is declared in the
|
|
||||||
// Header frame
|
|
||||||
func (f *BodyFrame) write(w io.Writer) (err error) {
|
|
||||||
return writeFrame(w, frameBody, f.ChannelId, f.Body)
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeFrame(w io.Writer, typ uint8, channel uint16, payload []byte) (err error) {
|
|
||||||
end := []byte{frameEnd}
|
|
||||||
size := uint(len(payload))
|
|
||||||
|
|
||||||
_, err = w.Write([]byte{
|
|
||||||
byte(typ),
|
|
||||||
byte((channel & 0xff00) >> 8),
|
|
||||||
byte((channel & 0x00ff) >> 0),
|
|
||||||
byte((size & 0xff000000) >> 24),
|
|
||||||
byte((size & 0x00ff0000) >> 16),
|
|
||||||
byte((size & 0x0000ff00) >> 8),
|
|
||||||
byte((size & 0x000000ff) >> 0),
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = w.Write(payload); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = w.Write(end); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeShortstr(w io.Writer, s string) (err error) {
|
|
||||||
b := []byte(s)
|
|
||||||
|
|
||||||
var length = uint8(len(b))
|
|
||||||
|
|
||||||
if err = binary.Write(w, binary.BigEndian, length); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = w.Write(b[:length]); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeLongstr(w io.Writer, s string) (err error) {
|
|
||||||
b := []byte(s)
|
|
||||||
|
|
||||||
var length = uint32(len(b))
|
|
||||||
|
|
||||||
if err = binary.Write(w, binary.BigEndian, length); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = w.Write(b[:length]); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
'A': []interface{}
|
|
||||||
'D': Decimal
|
|
||||||
'F': Table
|
|
||||||
'I': int32
|
|
||||||
'S': string
|
|
||||||
'T': time.Time
|
|
||||||
'V': nil
|
|
||||||
'b': byte
|
|
||||||
'd': float64
|
|
||||||
'f': float32
|
|
||||||
'l': int64
|
|
||||||
's': int16
|
|
||||||
't': bool
|
|
||||||
'x': []byte
|
|
||||||
*/
|
|
||||||
func writeField(w io.Writer, value interface{}) (err error) {
|
|
||||||
var buf [9]byte
|
|
||||||
var enc []byte
|
|
||||||
|
|
||||||
switch v := value.(type) {
|
|
||||||
case bool:
|
|
||||||
buf[0] = 't'
|
|
||||||
if v {
|
|
||||||
buf[1] = byte(1)
|
|
||||||
} else {
|
|
||||||
buf[1] = byte(0)
|
|
||||||
}
|
|
||||||
enc = buf[:2]
|
|
||||||
|
|
||||||
case byte:
|
|
||||||
buf[0] = 'b'
|
|
||||||
buf[1] = byte(v)
|
|
||||||
enc = buf[:2]
|
|
||||||
|
|
||||||
case int16:
|
|
||||||
buf[0] = 's'
|
|
||||||
binary.BigEndian.PutUint16(buf[1:3], uint16(v))
|
|
||||||
enc = buf[:3]
|
|
||||||
|
|
||||||
case int:
|
|
||||||
buf[0] = 'I'
|
|
||||||
binary.BigEndian.PutUint32(buf[1:5], uint32(v))
|
|
||||||
enc = buf[:5]
|
|
||||||
|
|
||||||
case int32:
|
|
||||||
buf[0] = 'I'
|
|
||||||
binary.BigEndian.PutUint32(buf[1:5], uint32(v))
|
|
||||||
enc = buf[:5]
|
|
||||||
|
|
||||||
case int64:
|
|
||||||
buf[0] = 'l'
|
|
||||||
binary.BigEndian.PutUint64(buf[1:9], uint64(v))
|
|
||||||
enc = buf[:9]
|
|
||||||
|
|
||||||
case float32:
|
|
||||||
buf[0] = 'f'
|
|
||||||
binary.BigEndian.PutUint32(buf[1:5], math.Float32bits(v))
|
|
||||||
enc = buf[:5]
|
|
||||||
|
|
||||||
case float64:
|
|
||||||
buf[0] = 'd'
|
|
||||||
binary.BigEndian.PutUint64(buf[1:9], math.Float64bits(v))
|
|
||||||
enc = buf[:9]
|
|
||||||
|
|
||||||
case Decimal:
|
|
||||||
buf[0] = 'D'
|
|
||||||
buf[1] = byte(v.Scale)
|
|
||||||
binary.BigEndian.PutUint32(buf[2:6], uint32(v.Value))
|
|
||||||
enc = buf[:6]
|
|
||||||
|
|
||||||
case string:
|
|
||||||
buf[0] = 'S'
|
|
||||||
binary.BigEndian.PutUint32(buf[1:5], uint32(len(v)))
|
|
||||||
enc = append(buf[:5], []byte(v)...)
|
|
||||||
|
|
||||||
case []interface{}: // field-array
|
|
||||||
buf[0] = 'A'
|
|
||||||
|
|
||||||
sec := new(bytes.Buffer)
|
|
||||||
for _, val := range v {
|
|
||||||
if err = writeField(sec, val); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
binary.BigEndian.PutUint32(buf[1:5], uint32(sec.Len()))
|
|
||||||
if _, err = w.Write(buf[:5]); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = w.Write(sec.Bytes()); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
case time.Time:
|
|
||||||
buf[0] = 'T'
|
|
||||||
binary.BigEndian.PutUint64(buf[1:9], uint64(v.Unix()))
|
|
||||||
enc = buf[:9]
|
|
||||||
|
|
||||||
case Table:
|
|
||||||
if _, err = w.Write([]byte{'F'}); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return writeTable(w, v)
|
|
||||||
|
|
||||||
case []byte:
|
|
||||||
buf[0] = 'x'
|
|
||||||
binary.BigEndian.PutUint32(buf[1:5], uint32(len(v)))
|
|
||||||
if _, err = w.Write(buf[0:5]); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, err = w.Write(v); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
case nil:
|
|
||||||
buf[0] = 'V'
|
|
||||||
enc = buf[:1]
|
|
||||||
|
|
||||||
default:
|
|
||||||
return ErrFieldType
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = w.Write(enc)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeTable(w io.Writer, table Table) (err error) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
|
|
||||||
for key, val := range table {
|
|
||||||
if err = writeShortstr(&buf, key); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = writeField(&buf, val); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return writeLongstr(w, buf.String())
|
|
||||||
}
|
|
||||||
@@ -13,4 +13,4 @@ test-pull-bin:
|
|||||||
|
|
||||||
test-pull-expect:
|
test-pull-expect:
|
||||||
@mkdir -p expect
|
@mkdir -p expect
|
||||||
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect/http/\* expect
|
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect2/http/\* expect
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func replaceForwardedFor(item *api.OutputChannelItem) {
|
|||||||
item.ConnectionInfo.ClientPort = ""
|
item.ConnectionInfo.ClientPort = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleHTTP2Stream(http2Assembler *Http2Assembler, tcpID *api.TcpID, superTimer *api.SuperTimer, emitter api.Emitter, options *api.TrafficFilteringOptions) error {
|
func handleHTTP2Stream(http2Assembler *Http2Assembler, tcpID *api.TcpID, superTimer *api.SuperTimer, emitter api.Emitter, options *api.TrafficFilteringOptions, reqResMatcher *requestResponseMatcher) error {
|
||||||
streamID, messageHTTP1, isGrpc, err := http2Assembler.readMessage()
|
streamID, messageHTTP1, isGrpc, err := http2Assembler.readMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -58,7 +58,7 @@ func handleHTTP2Stream(http2Assembler *Http2Assembler, tcpID *api.TcpID, superTi
|
|||||||
switch messageHTTP1 := messageHTTP1.(type) {
|
switch messageHTTP1 := messageHTTP1.(type) {
|
||||||
case http.Request:
|
case http.Request:
|
||||||
ident := fmt.Sprintf(
|
ident := fmt.Sprintf(
|
||||||
"%s->%s %s->%s %d %s",
|
"%s_%s_%s_%s_%d_%s",
|
||||||
tcpID.SrcIP,
|
tcpID.SrcIP,
|
||||||
tcpID.DstIP,
|
tcpID.DstIP,
|
||||||
tcpID.SrcPort,
|
tcpID.SrcPort,
|
||||||
@@ -78,7 +78,7 @@ func handleHTTP2Stream(http2Assembler *Http2Assembler, tcpID *api.TcpID, superTi
|
|||||||
}
|
}
|
||||||
case http.Response:
|
case http.Response:
|
||||||
ident := fmt.Sprintf(
|
ident := fmt.Sprintf(
|
||||||
"%s->%s %s->%s %d %s",
|
"%s_%s_%s_%s_%d_%s",
|
||||||
tcpID.DstIP,
|
tcpID.DstIP,
|
||||||
tcpID.SrcIP,
|
tcpID.SrcIP,
|
||||||
tcpID.DstPort,
|
tcpID.DstPort,
|
||||||
@@ -110,7 +110,7 @@ func handleHTTP2Stream(http2Assembler *Http2Assembler, tcpID *api.TcpID, superTi
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleHTTP1ClientStream(b *bufio.Reader, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, emitter api.Emitter, options *api.TrafficFilteringOptions) (switchingProtocolsHTTP2 bool, req *http.Request, err error) {
|
func handleHTTP1ClientStream(b *bufio.Reader, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, emitter api.Emitter, options *api.TrafficFilteringOptions, reqResMatcher *requestResponseMatcher) (switchingProtocolsHTTP2 bool, req *http.Request, err error) {
|
||||||
req, err = http.ReadRequest(b)
|
req, err = http.ReadRequest(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -130,8 +130,7 @@ func handleHTTP1ClientStream(b *bufio.Reader, tcpID *api.TcpID, counterPair *api
|
|||||||
req.Body = io.NopCloser(bytes.NewBuffer(body)) // rewind
|
req.Body = io.NopCloser(bytes.NewBuffer(body)) // rewind
|
||||||
|
|
||||||
ident := fmt.Sprintf(
|
ident := fmt.Sprintf(
|
||||||
"%d_%s:%s_%s:%s_%d_%s",
|
"%s_%s_%s_%s_%d_%s",
|
||||||
counterPair.StreamId,
|
|
||||||
tcpID.SrcIP,
|
tcpID.SrcIP,
|
||||||
tcpID.DstIP,
|
tcpID.DstIP,
|
||||||
tcpID.SrcPort,
|
tcpID.SrcPort,
|
||||||
@@ -153,7 +152,7 @@ func handleHTTP1ClientStream(b *bufio.Reader, tcpID *api.TcpID, counterPair *api
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleHTTP1ServerStream(b *bufio.Reader, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, emitter api.Emitter, options *api.TrafficFilteringOptions) (switchingProtocolsHTTP2 bool, err error) {
|
func handleHTTP1ServerStream(b *bufio.Reader, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, emitter api.Emitter, options *api.TrafficFilteringOptions, reqResMatcher *requestResponseMatcher) (switchingProtocolsHTTP2 bool, err error) {
|
||||||
var res *http.Response
|
var res *http.Response
|
||||||
res, err = http.ReadResponse(b, nil)
|
res, err = http.ReadResponse(b, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -174,8 +173,7 @@ func handleHTTP1ServerStream(b *bufio.Reader, tcpID *api.TcpID, counterPair *api
|
|||||||
res.Body = io.NopCloser(bytes.NewBuffer(body)) // rewind
|
res.Body = io.NopCloser(bytes.NewBuffer(body)) // rewind
|
||||||
|
|
||||||
ident := fmt.Sprintf(
|
ident := fmt.Sprintf(
|
||||||
"%d_%s:%s_%s:%s_%d_%s",
|
"%s_%s_%s_%s_%d_%s",
|
||||||
counterPair.StreamId,
|
|
||||||
tcpID.DstIP,
|
tcpID.DstIP,
|
||||||
tcpID.SrcIP,
|
tcpID.SrcIP,
|
||||||
tcpID.DstPort,
|
tcpID.DstPort,
|
||||||
|
|||||||
@@ -76,22 +76,19 @@ const (
|
|||||||
TypeHttpResponse
|
TypeHttpResponse
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
log.Println("Initializing HTTP extension...")
|
|
||||||
}
|
|
||||||
|
|
||||||
type dissecting string
|
type dissecting string
|
||||||
|
|
||||||
func (d dissecting) Register(extension *api.Extension) {
|
func (d dissecting) Register(extension *api.Extension) {
|
||||||
extension.Protocol = &http11protocol
|
extension.Protocol = &http11protocol
|
||||||
extension.MatcherMap = reqResMatcher.openMessagesMap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d dissecting) Ping() {
|
func (d dissecting) Ping() {
|
||||||
log.Printf("pong %s", http11protocol.Name)
|
log.Printf("pong %s", http11protocol.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, superIdentifier *api.SuperIdentifier, emitter api.Emitter, options *api.TrafficFilteringOptions) error {
|
func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, superIdentifier *api.SuperIdentifier, emitter api.Emitter, options *api.TrafficFilteringOptions, _reqResMatcher api.RequestResponseMatcher) error {
|
||||||
|
reqResMatcher := _reqResMatcher.(*requestResponseMatcher)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
isHTTP2, _ := checkIsHTTP2Connection(b, isClient)
|
isHTTP2, _ := checkIsHTTP2Connection(b, isClient)
|
||||||
|
|
||||||
@@ -124,7 +121,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
|
|||||||
}
|
}
|
||||||
|
|
||||||
if isHTTP2 {
|
if isHTTP2 {
|
||||||
err = handleHTTP2Stream(http2Assembler, tcpID, superTimer, emitter, options)
|
err = handleHTTP2Stream(http2Assembler, tcpID, superTimer, emitter, options, reqResMatcher)
|
||||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
@@ -133,7 +130,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
|
|||||||
superIdentifier.Protocol = &http11protocol
|
superIdentifier.Protocol = &http11protocol
|
||||||
} else if isClient {
|
} else if isClient {
|
||||||
var req *http.Request
|
var req *http.Request
|
||||||
switchingProtocolsHTTP2, req, err = handleHTTP1ClientStream(b, tcpID, counterPair, superTimer, emitter, options)
|
switchingProtocolsHTTP2, req, err = handleHTTP1ClientStream(b, tcpID, counterPair, superTimer, emitter, options, reqResMatcher)
|
||||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
@@ -144,7 +141,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
|
|||||||
// In case of an HTTP2 upgrade, duplicate the HTTP1 request into HTTP2 with stream ID 1
|
// In case of an HTTP2 upgrade, duplicate the HTTP1 request into HTTP2 with stream ID 1
|
||||||
if switchingProtocolsHTTP2 {
|
if switchingProtocolsHTTP2 {
|
||||||
ident := fmt.Sprintf(
|
ident := fmt.Sprintf(
|
||||||
"%s->%s %s->%s 1 %s",
|
"%s_%s_%s_%s_1_%s",
|
||||||
tcpID.SrcIP,
|
tcpID.SrcIP,
|
||||||
tcpID.DstIP,
|
tcpID.DstIP,
|
||||||
tcpID.SrcPort,
|
tcpID.SrcPort,
|
||||||
@@ -164,7 +161,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switchingProtocolsHTTP2, err = handleHTTP1ServerStream(b, tcpID, counterPair, superTimer, emitter, options)
|
switchingProtocolsHTTP2, err = handleHTTP1ServerStream(b, tcpID, counterPair, superTimer, emitter, options, reqResMatcher)
|
||||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
@@ -181,7 +178,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string, resolvedDestination string) *api.Entry {
|
func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string, resolvedDestination string, namespace string) *api.Entry {
|
||||||
var host, authority, path string
|
var host, authority, path string
|
||||||
|
|
||||||
request := item.Pair.Request.Payload.(map[string]interface{})
|
request := item.Pair.Request.Payload.(map[string]interface{})
|
||||||
@@ -279,6 +276,7 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
|
|||||||
IP: item.ConnectionInfo.ServerIP,
|
IP: item.ConnectionInfo.ServerIP,
|
||||||
Port: item.ConnectionInfo.ServerPort,
|
Port: item.ConnectionInfo.ServerPort,
|
||||||
},
|
},
|
||||||
|
Namespace: namespace,
|
||||||
Outgoing: item.ConnectionInfo.IsOutgoing,
|
Outgoing: item.ConnectionInfo.IsOutgoing,
|
||||||
Request: reqDetails,
|
Request: reqDetails,
|
||||||
Response: resDetails,
|
Response: resDetails,
|
||||||
@@ -472,6 +470,10 @@ func (d dissecting) Macros() map[string]string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d dissecting) NewResponseRequestMatcher() api.RequestResponseMatcher {
|
||||||
|
return createResponseRequestMatcher()
|
||||||
|
}
|
||||||
|
|
||||||
var Dissector dissecting
|
var Dissector dissecting
|
||||||
|
|
||||||
func NewDissector() api.Dissector {
|
func NewDissector() api.Dissector {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -39,7 +38,6 @@ func TestRegister(t *testing.T) {
|
|||||||
extension := &api.Extension{}
|
extension := &api.Extension{}
|
||||||
dissector.Register(extension)
|
dissector.Register(extension)
|
||||||
assert.Equal(t, "http", extension.Protocol.Name)
|
assert.Equal(t, "http", extension.Protocol.Name)
|
||||||
assert.NotNil(t, extension.MatcherMap)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMacros(t *testing.T) {
|
func TestMacros(t *testing.T) {
|
||||||
@@ -123,7 +121,8 @@ func TestDissect(t *testing.T) {
|
|||||||
SrcPort: "1",
|
SrcPort: "1",
|
||||||
DstPort: "2",
|
DstPort: "2",
|
||||||
}
|
}
|
||||||
err = dissector.Dissect(bufferClient, true, tcpIDClient, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options)
|
reqResMatcher := dissector.NewResponseRequestMatcher()
|
||||||
|
err = dissector.Dissect(bufferClient, true, tcpIDClient, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher)
|
||||||
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
|
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -141,7 +140,7 @@ func TestDissect(t *testing.T) {
|
|||||||
SrcPort: "2",
|
SrcPort: "2",
|
||||||
DstPort: "1",
|
DstPort: "1",
|
||||||
}
|
}
|
||||||
err = dissector.Dissect(bufferServer, false, tcpIDServer, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options)
|
err = dissector.Dissect(bufferServer, false, tcpIDServer, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher)
|
||||||
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
|
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -155,14 +154,6 @@ func TestDissect(t *testing.T) {
|
|||||||
|
|
||||||
stop <- true
|
stop <- true
|
||||||
|
|
||||||
sort.Slice(items, func(i, j int) bool {
|
|
||||||
iMarshaled, err := json.Marshal(items[i])
|
|
||||||
assert.Nil(t, err)
|
|
||||||
jMarshaled, err := json.Marshal(items[j])
|
|
||||||
assert.Nil(t, err)
|
|
||||||
return len(iMarshaled) < len(jMarshaled)
|
|
||||||
})
|
|
||||||
|
|
||||||
marshaled, err := json.Marshal(items)
|
marshaled, err := json.Marshal(items)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -214,7 +205,7 @@ func TestAnalyze(t *testing.T) {
|
|||||||
|
|
||||||
var entries []*api.Entry
|
var entries []*api.Entry
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
entry := dissector.Analyze(item, "", "")
|
entry := dissector.Analyze(item, "", "", "")
|
||||||
entries = append(entries, entry)
|
entries = append(entries, entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,16 +8,20 @@ import (
|
|||||||
"github.com/up9inc/mizu/tap/api"
|
"github.com/up9inc/mizu/tap/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
var reqResMatcher = createResponseRequestMatcher() // global
|
// Key is {client_addr}_{client_port}_{dest_addr}_{dest_port}_{incremental_counter}_{proto_ident}
|
||||||
|
|
||||||
// Key is {client_addr}:{client_port}->{dest_addr}:{dest_port}_{incremental_counter}
|
|
||||||
type requestResponseMatcher struct {
|
type requestResponseMatcher struct {
|
||||||
openMessagesMap *sync.Map
|
openMessagesMap *sync.Map
|
||||||
}
|
}
|
||||||
|
|
||||||
func createResponseRequestMatcher() requestResponseMatcher {
|
func createResponseRequestMatcher() api.RequestResponseMatcher {
|
||||||
newMatcher := &requestResponseMatcher{openMessagesMap: &sync.Map{}}
|
return &requestResponseMatcher{openMessagesMap: &sync.Map{}}
|
||||||
return *newMatcher
|
}
|
||||||
|
|
||||||
|
func (matcher *requestResponseMatcher) GetMap() *sync.Map {
|
||||||
|
return matcher.openMessagesMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (matcher *requestResponseMatcher) SetMaxTry(value int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (matcher *requestResponseMatcher) registerRequest(ident string, request *http.Request, captureTime time.Time, protoMinor int) *api.OutputChannelItem {
|
func (matcher *requestResponseMatcher) registerRequest(ident string, request *http.Request, captureTime time.Time, protoMinor int) *api.OutputChannelItem {
|
||||||
|
|||||||
16
tap/extensions/kafka/Makefile
Normal file
16
tap/extensions/kafka/Makefile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
skipbin := $$(find bin -mindepth 1 -maxdepth 1)
|
||||||
|
skipexpect := $$(find expect -mindepth 1 -maxdepth 1)
|
||||||
|
|
||||||
|
test: test-pull-bin test-pull-expect
|
||||||
|
@MIZU_TEST=1 go test -v ./... -coverpkg=./... -race -coverprofile=coverage.out -covermode=atomic
|
||||||
|
|
||||||
|
test-update: test-pull-bin
|
||||||
|
@MIZU_TEST=1 TEST_UPDATE=1 go test -v ./... -coverpkg=./... -coverprofile=coverage.out -covermode=atomic
|
||||||
|
|
||||||
|
test-pull-bin:
|
||||||
|
@mkdir -p bin
|
||||||
|
@[ "${skipbin}" ] && echo "Skipping downloading BINs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp gs://static.up9.io/mizu/test-pcap/bin/kafka/\*.bin bin
|
||||||
|
|
||||||
|
test-pull-expect:
|
||||||
|
@mkdir -p expect
|
||||||
|
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect/kafka/\* expect
|
||||||
@@ -1,584 +0,0 @@
|
|||||||
package kafka
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Bytes is an interface implemented by types that represent immutable
|
|
||||||
// sequences of bytes.
|
|
||||||
//
|
|
||||||
// Bytes values are used to abstract the location where record keys and
|
|
||||||
// values are read from (e.g. in-memory buffers, network sockets, files).
|
|
||||||
//
|
|
||||||
// The Close method should be called to release resources held by the object
|
|
||||||
// when the program is done with it.
|
|
||||||
//
|
|
||||||
// Bytes values are generally not safe to use concurrently from multiple
|
|
||||||
// goroutines.
|
|
||||||
type Bytes interface {
|
|
||||||
io.ReadCloser
|
|
||||||
// Returns the number of bytes remaining to be read from the payload.
|
|
||||||
Len() int
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBytes constructs a Bytes value from b.
|
|
||||||
//
|
|
||||||
// The returned value references b, it does not make a copy of the backing
|
|
||||||
// array.
|
|
||||||
//
|
|
||||||
// If b is nil, nil is returned to represent a null BYTES value in the kafka
|
|
||||||
// protocol.
|
|
||||||
func NewBytes(b []byte) Bytes {
|
|
||||||
if b == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
r := new(bytesReader)
|
|
||||||
r.Reset(b)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadAll is similar to ioutil.ReadAll, but it takes advantage of knowing the
|
|
||||||
// length of b to minimize the memory footprint.
|
|
||||||
//
|
|
||||||
// The function returns a nil slice if b is nil.
|
|
||||||
// func ReadAll(b Bytes) ([]byte, error) {
|
|
||||||
// if b == nil {
|
|
||||||
// return nil, nil
|
|
||||||
// }
|
|
||||||
// s := make([]byte, b.Len())
|
|
||||||
// _, err := io.ReadFull(b, s)
|
|
||||||
// return s, err
|
|
||||||
// }
|
|
||||||
|
|
||||||
type bytesReader struct{ bytes.Reader }
|
|
||||||
|
|
||||||
func (*bytesReader) Close() error { return nil }
|
|
||||||
|
|
||||||
type refCount uintptr
|
|
||||||
|
|
||||||
func (rc *refCount) ref() { atomic.AddUintptr((*uintptr)(rc), 1) }
|
|
||||||
|
|
||||||
func (rc *refCount) unref(onZero func()) {
|
|
||||||
if atomic.AddUintptr((*uintptr)(rc), ^uintptr(0)) == 0 {
|
|
||||||
onZero()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Size of the memory buffer for a single page. We use a farily
|
|
||||||
// large size here (64 KiB) because batches exchanged with kafka
|
|
||||||
// tend to be multiple kilobytes in size, sometimes hundreds.
|
|
||||||
// Using large pages amortizes the overhead of the page metadata
|
|
||||||
// and algorithms to manage the pages.
|
|
||||||
pageSize = 65536
|
|
||||||
)
|
|
||||||
|
|
||||||
type page struct {
|
|
||||||
refc refCount
|
|
||||||
offset int64
|
|
||||||
length int
|
|
||||||
buffer *[pageSize]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPage(offset int64) *page {
|
|
||||||
p, _ := pagePool.Get().(*page)
|
|
||||||
if p != nil {
|
|
||||||
p.offset = offset
|
|
||||||
p.length = 0
|
|
||||||
p.ref()
|
|
||||||
} else {
|
|
||||||
p = &page{
|
|
||||||
refc: 1,
|
|
||||||
offset: offset,
|
|
||||||
buffer: &[pageSize]byte{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *page) ref() { p.refc.ref() }
|
|
||||||
|
|
||||||
func (p *page) unref() { p.refc.unref(func() { pagePool.Put(p) }) }
|
|
||||||
|
|
||||||
func (p *page) slice(begin, end int64) []byte {
|
|
||||||
i, j := begin-p.offset, end-p.offset
|
|
||||||
|
|
||||||
if i < 0 {
|
|
||||||
i = 0
|
|
||||||
} else if i > pageSize {
|
|
||||||
i = pageSize
|
|
||||||
}
|
|
||||||
|
|
||||||
if j < 0 {
|
|
||||||
j = 0
|
|
||||||
} else if j > pageSize {
|
|
||||||
j = pageSize
|
|
||||||
}
|
|
||||||
|
|
||||||
if i < j {
|
|
||||||
return p.buffer[i:j]
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *page) Cap() int { return pageSize }
|
|
||||||
|
|
||||||
func (p *page) Len() int { return p.length }
|
|
||||||
|
|
||||||
func (p *page) Size() int64 { return int64(p.length) }
|
|
||||||
|
|
||||||
func (p *page) Truncate(n int) {
|
|
||||||
if n < p.length {
|
|
||||||
p.length = n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *page) ReadAt(b []byte, off int64) (int, error) {
|
|
||||||
if off -= p.offset; off < 0 || off > pageSize {
|
|
||||||
panic("offset out of range")
|
|
||||||
}
|
|
||||||
if off > int64(p.length) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
return copy(b, p.buffer[off:p.length]), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *page) ReadFrom(r io.Reader) (int64, error) {
|
|
||||||
n, err := io.ReadFull(r, p.buffer[p.length:])
|
|
||||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
p.length += n
|
|
||||||
return int64(n), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *page) WriteAt(b []byte, off int64) (int, error) {
|
|
||||||
if off -= p.offset; off < 0 || off > pageSize {
|
|
||||||
panic("offset out of range")
|
|
||||||
}
|
|
||||||
n := copy(p.buffer[off:], b)
|
|
||||||
if end := int(off) + n; end > p.length {
|
|
||||||
p.length = end
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *page) Write(b []byte) (int, error) {
|
|
||||||
return p.WriteAt(b, p.offset+int64(p.length))
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ io.ReaderAt = (*page)(nil)
|
|
||||||
_ io.ReaderFrom = (*page)(nil)
|
|
||||||
_ io.Writer = (*page)(nil)
|
|
||||||
_ io.WriterAt = (*page)(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
type pageBuffer struct {
|
|
||||||
refc refCount
|
|
||||||
pages contiguousPages
|
|
||||||
length int
|
|
||||||
cursor int
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPageBuffer() *pageBuffer {
|
|
||||||
b, _ := pageBufferPool.Get().(*pageBuffer)
|
|
||||||
if b != nil {
|
|
||||||
b.cursor = 0
|
|
||||||
b.refc.ref()
|
|
||||||
} else {
|
|
||||||
b = &pageBuffer{
|
|
||||||
refc: 1,
|
|
||||||
pages: make(contiguousPages, 0, 16),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pb *pageBuffer) unref() {
|
|
||||||
pb.refc.unref(func() {
|
|
||||||
pb.pages.unref()
|
|
||||||
pb.pages.clear()
|
|
||||||
pb.pages = pb.pages[:0]
|
|
||||||
pb.length = 0
|
|
||||||
pageBufferPool.Put(pb)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pb *pageBuffer) newPage() *page {
|
|
||||||
return newPage(int64(pb.length))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pb *pageBuffer) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pb *pageBuffer) Len() int {
|
|
||||||
return pb.length - pb.cursor
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pb *pageBuffer) Size() int64 {
|
|
||||||
return int64(pb.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pb *pageBuffer) Discard(n int) (int, error) {
|
|
||||||
remain := pb.length - pb.cursor
|
|
||||||
if remain < n {
|
|
||||||
n = remain
|
|
||||||
}
|
|
||||||
pb.cursor += n
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pb *pageBuffer) Truncate(n int) {
|
|
||||||
if n < pb.length {
|
|
||||||
pb.length = n
|
|
||||||
|
|
||||||
if n < pb.cursor {
|
|
||||||
pb.cursor = n
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range pb.pages {
|
|
||||||
if p := pb.pages[i]; p.length <= n {
|
|
||||||
n -= p.length
|
|
||||||
} else {
|
|
||||||
if n > 0 {
|
|
||||||
pb.pages[i].Truncate(n)
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
pb.pages[i:].unref()
|
|
||||||
pb.pages[i:].clear()
|
|
||||||
pb.pages = pb.pages[:i]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pb *pageBuffer) Seek(offset int64, whence int) (int64, error) {
|
|
||||||
c, err := seek(int64(pb.cursor), int64(pb.length), offset, whence)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
pb.cursor = int(c)
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pb *pageBuffer) ReadByte() (byte, error) {
|
|
||||||
b := [1]byte{}
|
|
||||||
_, err := pb.Read(b[:])
|
|
||||||
return b[0], err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pb *pageBuffer) Read(b []byte) (int, error) {
|
|
||||||
if pb.cursor >= pb.length {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
n, err := pb.ReadAt(b, int64(pb.cursor))
|
|
||||||
pb.cursor += n
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pb *pageBuffer) ReadAt(b []byte, off int64) (int, error) {
|
|
||||||
return pb.pages.ReadAt(b, off)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pb *pageBuffer) ReadFrom(r io.Reader) (int64, error) {
|
|
||||||
if len(pb.pages) == 0 {
|
|
||||||
pb.pages = append(pb.pages, pb.newPage())
|
|
||||||
}
|
|
||||||
|
|
||||||
rn := int64(0)
|
|
||||||
|
|
||||||
for {
|
|
||||||
tail := pb.pages[len(pb.pages)-1]
|
|
||||||
free := tail.Cap() - tail.Len()
|
|
||||||
|
|
||||||
if free == 0 {
|
|
||||||
tail = pb.newPage()
|
|
||||||
free = pageSize
|
|
||||||
pb.pages = append(pb.pages, tail)
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := tail.ReadFrom(r)
|
|
||||||
pb.length += int(n)
|
|
||||||
rn += n
|
|
||||||
if n < int64(free) {
|
|
||||||
return rn, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pb *pageBuffer) WriteString(s string) (int, error) {
|
|
||||||
return pb.Write([]byte(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pb *pageBuffer) Write(b []byte) (int, error) {
|
|
||||||
wn := len(b)
|
|
||||||
if wn == 0 {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(pb.pages) == 0 {
|
|
||||||
pb.pages = append(pb.pages, pb.newPage())
|
|
||||||
}
|
|
||||||
|
|
||||||
for len(b) != 0 {
|
|
||||||
tail := pb.pages[len(pb.pages)-1]
|
|
||||||
free := tail.Cap() - tail.Len()
|
|
||||||
|
|
||||||
if len(b) <= free {
|
|
||||||
_, _ = tail.Write(b)
|
|
||||||
pb.length += len(b)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _ = tail.Write(b[:free])
|
|
||||||
b = b[free:]
|
|
||||||
|
|
||||||
pb.length += free
|
|
||||||
pb.pages = append(pb.pages, pb.newPage())
|
|
||||||
}
|
|
||||||
|
|
||||||
return wn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pb *pageBuffer) WriteAt(b []byte, off int64) (int, error) {
|
|
||||||
n, err := pb.pages.WriteAt(b, off)
|
|
||||||
if err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
if n < len(b) {
|
|
||||||
_, _ = pb.Write(b[n:])
|
|
||||||
}
|
|
||||||
return len(b), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pb *pageBuffer) WriteTo(w io.Writer) (int64, error) {
|
|
||||||
var wn int
|
|
||||||
var err error
|
|
||||||
pb.pages.scan(int64(pb.cursor), int64(pb.length), func(b []byte) bool {
|
|
||||||
var n int
|
|
||||||
n, err = w.Write(b)
|
|
||||||
wn += n
|
|
||||||
return err == nil
|
|
||||||
})
|
|
||||||
pb.cursor += wn
|
|
||||||
return int64(wn), err
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ io.ReaderAt = (*pageBuffer)(nil)
|
|
||||||
_ io.ReaderFrom = (*pageBuffer)(nil)
|
|
||||||
_ io.StringWriter = (*pageBuffer)(nil)
|
|
||||||
_ io.Writer = (*pageBuffer)(nil)
|
|
||||||
_ io.WriterAt = (*pageBuffer)(nil)
|
|
||||||
_ io.WriterTo = (*pageBuffer)(nil)
|
|
||||||
|
|
||||||
pagePool sync.Pool
|
|
||||||
pageBufferPool sync.Pool
|
|
||||||
)
|
|
||||||
|
|
||||||
type contiguousPages []*page
|
|
||||||
|
|
||||||
func (pages contiguousPages) unref() {
|
|
||||||
for _, p := range pages {
|
|
||||||
p.unref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pages contiguousPages) clear() {
|
|
||||||
for i := range pages {
|
|
||||||
pages[i] = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pages contiguousPages) ReadAt(b []byte, off int64) (int, error) {
|
|
||||||
rn := 0
|
|
||||||
|
|
||||||
for _, p := range pages.slice(off, off+int64(len(b))) {
|
|
||||||
n, _ := p.ReadAt(b, off)
|
|
||||||
b = b[n:]
|
|
||||||
rn += n
|
|
||||||
off += int64(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
return rn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pages contiguousPages) WriteAt(b []byte, off int64) (int, error) {
|
|
||||||
wn := 0
|
|
||||||
|
|
||||||
for _, p := range pages.slice(off, off+int64(len(b))) {
|
|
||||||
n, _ := p.WriteAt(b, off)
|
|
||||||
b = b[n:]
|
|
||||||
wn += n
|
|
||||||
off += int64(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
return wn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pages contiguousPages) slice(begin, end int64) contiguousPages {
|
|
||||||
i := pages.indexOf(begin)
|
|
||||||
j := pages.indexOf(end)
|
|
||||||
if j < len(pages) {
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
return pages[i:j]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pages contiguousPages) indexOf(offset int64) int {
|
|
||||||
if len(pages) == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return int((offset - pages[0].offset) / pageSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pages contiguousPages) scan(begin, end int64, f func([]byte) bool) {
|
|
||||||
for _, p := range pages.slice(begin, end) {
|
|
||||||
if !f(p.slice(begin, end)) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ io.ReaderAt = contiguousPages{}
|
|
||||||
_ io.WriterAt = contiguousPages{}
|
|
||||||
)
|
|
||||||
|
|
||||||
type pageRef struct {
|
|
||||||
pages contiguousPages
|
|
||||||
offset int64
|
|
||||||
cursor int64
|
|
||||||
length uint32
|
|
||||||
once uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ref *pageRef) unref() {
|
|
||||||
if atomic.CompareAndSwapUint32(&ref.once, 0, 1) {
|
|
||||||
ref.pages.unref()
|
|
||||||
ref.pages.clear()
|
|
||||||
ref.pages = nil
|
|
||||||
ref.offset = 0
|
|
||||||
ref.cursor = 0
|
|
||||||
ref.length = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ref *pageRef) Len() int { return int(ref.Size() - ref.cursor) }
|
|
||||||
|
|
||||||
func (ref *pageRef) Size() int64 { return int64(ref.length) }
|
|
||||||
|
|
||||||
func (ref *pageRef) Close() error { ref.unref(); return nil }
|
|
||||||
|
|
||||||
func (ref *pageRef) String() string {
|
|
||||||
return fmt.Sprintf("[offset=%d cursor=%d length=%d]", ref.offset, ref.cursor, ref.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ref *pageRef) Seek(offset int64, whence int) (int64, error) {
|
|
||||||
c, err := seek(ref.cursor, int64(ref.length), offset, whence)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
ref.cursor = c
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ref *pageRef) ReadByte() (byte, error) {
|
|
||||||
var c byte
|
|
||||||
var ok bool
|
|
||||||
ref.scan(ref.cursor, func(b []byte) bool {
|
|
||||||
c, ok = b[0], true
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
if ok {
|
|
||||||
ref.cursor++
|
|
||||||
} else {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ref *pageRef) Read(b []byte) (int, error) {
|
|
||||||
if ref.cursor >= int64(ref.length) {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
n, err := ref.ReadAt(b, ref.cursor)
|
|
||||||
ref.cursor += int64(n)
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ref *pageRef) ReadAt(b []byte, off int64) (int, error) {
|
|
||||||
limit := ref.offset + int64(ref.length)
|
|
||||||
off += ref.offset
|
|
||||||
|
|
||||||
if off >= limit {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
if off+int64(len(b)) > limit {
|
|
||||||
b = b[:limit-off]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(b) == 0 {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := ref.pages.ReadAt(b, off)
|
|
||||||
if n == 0 && err == nil {
|
|
||||||
err = io.EOF
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ref *pageRef) WriteTo(w io.Writer) (wn int64, err error) {
|
|
||||||
ref.scan(ref.cursor, func(b []byte) bool {
|
|
||||||
var n int
|
|
||||||
n, err = w.Write(b)
|
|
||||||
wn += int64(n)
|
|
||||||
return err == nil
|
|
||||||
})
|
|
||||||
ref.cursor += wn
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ref *pageRef) scan(off int64, f func([]byte) bool) {
|
|
||||||
begin := ref.offset + off
|
|
||||||
end := ref.offset + int64(ref.length)
|
|
||||||
ref.pages.scan(begin, end, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ io.Closer = (*pageRef)(nil)
|
|
||||||
_ io.Seeker = (*pageRef)(nil)
|
|
||||||
_ io.Reader = (*pageRef)(nil)
|
|
||||||
_ io.ReaderAt = (*pageRef)(nil)
|
|
||||||
_ io.WriterTo = (*pageRef)(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
func seek(cursor, limit, offset int64, whence int) (int64, error) {
|
|
||||||
switch whence {
|
|
||||||
case io.SeekStart:
|
|
||||||
// absolute offset
|
|
||||||
case io.SeekCurrent:
|
|
||||||
offset = cursor + offset
|
|
||||||
case io.SeekEnd:
|
|
||||||
offset = limit - offset
|
|
||||||
default:
|
|
||||||
return -1, fmt.Errorf("seek: invalid whence value: %d", whence)
|
|
||||||
}
|
|
||||||
if offset < 0 {
|
|
||||||
offset = 0
|
|
||||||
}
|
|
||||||
if offset > limit {
|
|
||||||
offset = limit
|
|
||||||
}
|
|
||||||
return offset, nil
|
|
||||||
}
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
package kafka
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"text/tabwriter"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Cluster struct {
|
|
||||||
ClusterID string
|
|
||||||
Controller int32
|
|
||||||
Brokers map[int32]Broker
|
|
||||||
Topics map[string]Topic
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Cluster) BrokerIDs() []int32 {
|
|
||||||
brokerIDs := make([]int32, 0, len(c.Brokers))
|
|
||||||
for id := range c.Brokers {
|
|
||||||
brokerIDs = append(brokerIDs, id)
|
|
||||||
}
|
|
||||||
sort.Slice(brokerIDs, func(i, j int) bool {
|
|
||||||
return brokerIDs[i] < brokerIDs[j]
|
|
||||||
})
|
|
||||||
return brokerIDs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Cluster) TopicNames() []string {
|
|
||||||
topicNames := make([]string, 0, len(c.Topics))
|
|
||||||
for name := range c.Topics {
|
|
||||||
topicNames = append(topicNames, name)
|
|
||||||
}
|
|
||||||
sort.Strings(topicNames)
|
|
||||||
return topicNames
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Cluster) IsZero() bool {
|
|
||||||
return c.ClusterID == "" && c.Controller == 0 && len(c.Brokers) == 0 && len(c.Topics) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Cluster) Format(w fmt.State, _ rune) {
|
|
||||||
tw := new(tabwriter.Writer)
|
|
||||||
fmt.Fprintf(w, "CLUSTER: %q\n\n", c.ClusterID)
|
|
||||||
|
|
||||||
tw.Init(w, 0, 8, 2, ' ', 0)
|
|
||||||
fmt.Fprint(tw, " BROKER\tHOST\tPORT\tRACK\tCONTROLLER\n")
|
|
||||||
|
|
||||||
for _, id := range c.BrokerIDs() {
|
|
||||||
broker := c.Brokers[id]
|
|
||||||
fmt.Fprintf(tw, " %d\t%s\t%d\t%s\t%t\n", broker.ID, broker.Host, broker.Port, broker.Rack, broker.ID == c.Controller)
|
|
||||||
}
|
|
||||||
|
|
||||||
tw.Flush()
|
|
||||||
fmt.Fprintln(w)
|
|
||||||
|
|
||||||
tw.Init(w, 0, 8, 2, ' ', 0)
|
|
||||||
fmt.Fprint(tw, " TOPIC\tPARTITIONS\tBROKERS\n")
|
|
||||||
topicNames := c.TopicNames()
|
|
||||||
brokers := make(map[int32]struct{}, len(c.Brokers))
|
|
||||||
brokerIDs := make([]int32, 0, len(c.Brokers))
|
|
||||||
|
|
||||||
for _, name := range topicNames {
|
|
||||||
topic := c.Topics[name]
|
|
||||||
|
|
||||||
for _, p := range topic.Partitions {
|
|
||||||
for _, id := range p.Replicas {
|
|
||||||
brokers[id] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for id := range brokers {
|
|
||||||
brokerIDs = append(brokerIDs, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(tw, " %s\t%d\t%s\n", topic.Name, len(topic.Partitions), formatBrokerIDs(brokerIDs, -1))
|
|
||||||
|
|
||||||
for id := range brokers {
|
|
||||||
delete(brokers, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
brokerIDs = brokerIDs[:0]
|
|
||||||
}
|
|
||||||
|
|
||||||
tw.Flush()
|
|
||||||
fmt.Fprintln(w)
|
|
||||||
|
|
||||||
if w.Flag('+') {
|
|
||||||
for _, name := range topicNames {
|
|
||||||
fmt.Fprintf(w, " TOPIC: %q\n\n", name)
|
|
||||||
|
|
||||||
tw.Init(w, 0, 8, 2, ' ', 0)
|
|
||||||
fmt.Fprint(tw, " PARTITION\tREPLICAS\tISR\tOFFLINE\n")
|
|
||||||
|
|
||||||
for _, p := range c.Topics[name].Partitions {
|
|
||||||
fmt.Fprintf(tw, " %d\t%s\t%s\t%s\n", p.ID,
|
|
||||||
formatBrokerIDs(p.Replicas, -1),
|
|
||||||
formatBrokerIDs(p.ISR, p.Leader),
|
|
||||||
formatBrokerIDs(p.Offline, -1),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
tw.Flush()
|
|
||||||
fmt.Fprintln(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatBrokerIDs(brokerIDs []int32, leader int32) string {
|
|
||||||
if len(brokerIDs) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(brokerIDs) == 1 {
|
|
||||||
return itoa(brokerIDs[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Slice(brokerIDs, func(i, j int) bool {
|
|
||||||
id1 := brokerIDs[i]
|
|
||||||
id2 := brokerIDs[j]
|
|
||||||
|
|
||||||
if id1 == leader {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if id2 == leader {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return id1 < id2
|
|
||||||
})
|
|
||||||
|
|
||||||
brokerNames := make([]string, len(brokerIDs))
|
|
||||||
|
|
||||||
for i, id := range brokerIDs {
|
|
||||||
brokerNames[i] = itoa(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(brokerNames, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ fmt.Formatter = Cluster{}
|
|
||||||
)
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package kafka
|
package kafka
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
@@ -9,8 +8,6 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type discarder interface {
|
type discarder interface {
|
||||||
@@ -26,15 +23,6 @@ type decoder struct {
|
|||||||
crc32 uint32
|
crc32 uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) Reset(r io.Reader, n int) {
|
|
||||||
d.reader = r
|
|
||||||
d.remain = n
|
|
||||||
d.buffer = [8]byte{}
|
|
||||||
d.err = nil
|
|
||||||
d.table = nil
|
|
||||||
d.crc32 = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) Read(b []byte) (int, error) {
|
func (d *decoder) Read(b []byte) (int, error) {
|
||||||
if d.err != nil {
|
if d.err != nil {
|
||||||
return 0, d.err
|
return 0, d.err
|
||||||
@@ -483,52 +471,3 @@ func decodeReadInt32(b []byte) int32 {
|
|||||||
func decodeReadInt64(b []byte) int64 {
|
func decodeReadInt64(b []byte) int64 {
|
||||||
return int64(binary.BigEndian.Uint64(b))
|
return int64(binary.BigEndian.Uint64(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
func Unmarshal(data []byte, version int16, value interface{}) error {
|
|
||||||
typ := elemTypeOf(value)
|
|
||||||
cache, _ := unmarshalers.Load().(map[versionedType]decodeFunc)
|
|
||||||
key := versionedType{typ: typ, version: version}
|
|
||||||
decode := cache[key]
|
|
||||||
|
|
||||||
if decode == nil {
|
|
||||||
decode = decodeFuncOf(reflect.TypeOf(value).Elem(), version, false, structTag{
|
|
||||||
MinVersion: -1,
|
|
||||||
MaxVersion: -1,
|
|
||||||
TagID: -2,
|
|
||||||
Compact: true,
|
|
||||||
Nullable: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
newCache := make(map[versionedType]decodeFunc, len(cache)+1)
|
|
||||||
newCache[key] = decode
|
|
||||||
|
|
||||||
for typ, fun := range cache {
|
|
||||||
newCache[typ] = fun
|
|
||||||
}
|
|
||||||
|
|
||||||
unmarshalers.Store(newCache)
|
|
||||||
}
|
|
||||||
|
|
||||||
d, _ := decoders.Get().(*decoder)
|
|
||||||
if d == nil {
|
|
||||||
d = &decoder{reader: bytes.NewReader(nil)}
|
|
||||||
}
|
|
||||||
|
|
||||||
d.remain = len(data)
|
|
||||||
r, _ := d.reader.(*bytes.Reader)
|
|
||||||
r.Reset(data)
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
r.Reset(nil)
|
|
||||||
d.Reset(r, 0)
|
|
||||||
decoders.Put(d)
|
|
||||||
}()
|
|
||||||
|
|
||||||
decode(d, valueOf(value))
|
|
||||||
return dontExpectEOF(d.err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
decoders sync.Pool // *decoder
|
|
||||||
unmarshalers atomic.Value // map[versionedType]decodeFunc
|
|
||||||
)
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user