Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix open issues #4

Merged
merged 22 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
e2etest/artifacts/*
!e2etest/artifacts/.gitkeep
communication-matrix/*
commatrix-gen
19 changes: 17 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ CLUSTER_ENV ?= baremetal
DEST_DIR ?= .
DEPLOYMENT ?= mno
GO_SRC := cmd/main.go
OC_VERSION_TAG := 4.15.0-202402082307

EXECUTABLE := commatrix-gen

Expand All @@ -11,8 +12,22 @@ EXECUTABLE := commatrix-gen
build:
go build -o $(EXECUTABLE) $(GO_SRC)

# TODO: check if oc is installed
generate: build
oc:
ifeq (, $(shell which oc))
@{ \
set -e ;\
curl -LO https://github.com/openshift/oc/archive/refs/tags/openshift-clients-$(OC_VERSION_TAG).tar.gz ;\
tar -xf openshift-clients-$(OC_VERSION_TAG).tar.gz ;\
cd $(PWD)/oc-openshift-clients-$(OC_VERSION_TAG) ;\
make oc ;\
mv oc $(GOBIN)/oc ;\
chmod u+x $(GOBIN)/oc ;\
rm -rf $(PWD)/oc-openshift-clients-$(OC_VERSION_TAG) ;\
rm $(PWD)/openshift-clients-$(OC_VERSION_TAG).tar.gz ;\
}
endif

generate: oc build
mkdir -p $(DEST_DIR)/communication-matrix
./$(EXECUTABLE) -format=$(FORMAT) -env=$(CLUSTER_ENV) -destDir=$(DEST_DIR)/communication-matrix -deployment=$(DEPLOYMENT)

Expand Down
94 changes: 43 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,64 +1,56 @@
## commatrix

This go library provide tools to produce k8s node communication matrix, i.e.
a file that describes what ports the cluster listens to.
This project allows to automatically generate an accurate and up-to-date communication
flows matrix that can be delivered to customers as part of product documentation for all
ingress flows of OpenShift (multi-node and single-node deployments).

We produce this matrix from the existing EndpointSlieces, and in order to fetch
the relevant ones, the `endpointslices` package provide various querying methods.
### Usage of the EndpointSlice Resource

This library leverages the EndpointSlice resource to identify the ports the
cluster uses for ingress traffic. Relevant EndpointSlices include those
referencing host-networked pods, Node Port services, and LoadBalancer services.

### e2etest:
To invoke the e2etest, start by exporting the "KUBECONFIG" variable, and then run 'make e2etest.' This test will generate two matrices:
One from the EndpointSlices when the host services are manually produced using the 'customEndpointSlices.json' file.
The other matrix is generated by running 'ss' on the nodes.
The test is expected to fail. You can find the output of the 'ss' command for each node and protocol,
as well as the raw communication matrices in the 'e2etest/artifacts' directory, and the diff will be printed as part of the test output.

### Communication Matrix Creation Guide

The Communication Matrix is a structured list of Communication Details,
with each `ComDetails` entry representing a port. The fields for each entry
include `Direction` (currently "ingress" only), `Protocol` ("TCP" or "UDP"),
`Port` (number), `NodeRole` ("master" or "worker"), `ServiceName`,
and `Required` (false if optional).

Struct Definitions:
### Creating Custom ComDetails with ss Command

The `ss` command, a Linux utility, lists listening ports on
the host with `ss -anplt` for TCP or `ss -anplu` for UDP.
For example, consider the following ss entry:
```
type ComMatrix struct {
Matrix []ComDetails
}

type ComDetails struct {
Direction string `json:"direction"`
Protocol string `json:"protocol"`
Port string `json:"port"`
NodeRole string `json:"nodeRole"`
ServiceName string `json:"serviceName"`
Required bool `json:"required"`
}
LISTEN 0 4096 127.0.0.1:10248 0.0.0.0:* users:(("kubelet",pid=6187,fd=20))
```

#### Usage of EndpointSlice Resource
liornoy marked this conversation as resolved.
Show resolved Hide resolved

This library leverages the EndpointSlice resource to identify the ports the
cluster uses for ingress traffic. Relevant EndpointSlices include those
referencing host-networked pods, Node Port services, LoadBalancer services,
or any custom EndpointSlice labeled with `"ingress":""`.
The `ss` package provides the `CreateComDetailsFromNode` function that runs
the `ss` command on each node, and converts the output into a corresponding ComDetails list.

Explore the example in `/examples/query_endpointslices/main.go`.

#### Creating Custom ComDetails with ss Command

To encompass all ports Kubernetes nodes are listening to, querying existing
EndpointSlices may be insufficient. Not all services, like the SSH service,
are represented. The `ss` command, a Linux utility, lists listening ports on
the host with `ss -anplt` for TCP or `ss -anplu` for UDP.
### Communication Matrix Creation Guide

The `ss` package provides the `ToComDetails` function, converting `ss` command
output into a corresponding ComDetails list. Use the `ToEndpointSlice` method
to create an EndpointSlice object from this list.
Use the `generate` Makefile target to create the matrix.
The following environment variables are used to configure:
```
FORMAT (csv/json/yaml)
CLUSTER_ENV (baremetal/aws)
DEST_DIR (path to the directory containing the artifacts)
DEPLOYMENT (mno/sno)
```

As a convention, EndpointSlices referencing non-critical services are labeled with `"optional": ""`.
The generated artifcats are:
liornoy marked this conversation as resolved.
Show resolved Hide resolved
```
communication-matrix - The generated communication matrix.
ss-generated-matrix - The communication matrix that generated by the `ss` command.
matrix-diff-ss - Shows the variance between two matrices. Entries present in the communication matrix but absent in the ss matrix are marked with '+', while entries present in the ss matrix but not in the communication matrix are marked with '-'.
raw-ss-tcp - The raw `ss` output for TCP.
raw-ss-udp - The raw `ss` output for UDP.
```

Check the example in `/examples/create_custom_endpointslices/main.go` for a practical demonstration.
Each record describes a flow with the following information:
```
direction Data flow direction (currently ingress only)
protocol IP protocol (TCP/UDP/SCTP/etc)
port Flow port number
namespace EndpointSlice Namespace
service EndpointSlice owner Service name
pod EndpointSlice target Pod name
container Port owner Container name
nodeRole Service node host role (master/worker/master&worker[for SNO])
optional Optional or mandatory flow for OpenShift
```
40 changes: 33 additions & 7 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ import (
"path/filepath"
"sync"

"golang.org/x/sync/errgroup"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

clientutil "github.com/openshift-kni/commatrix/client"
"github.com/openshift-kni/commatrix/commatrix"
"github.com/openshift-kni/commatrix/consts"
"github.com/openshift-kni/commatrix/debug"
"github.com/openshift-kni/commatrix/ss"
"github.com/openshift-kni/commatrix/types"
"golang.org/x/sync/errgroup"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func main() {
Expand Down Expand Up @@ -82,7 +85,7 @@ func main() {
}

comMatrixFileName := filepath.Join(destDir, fmt.Sprintf("communication-matrix.%s", format))
err = os.WriteFile(comMatrixFileName, []byte(string(res)), 0644)
liornoy marked this conversation as resolved.
Show resolved Hide resolved
err = os.WriteFile(comMatrixFileName, res, 0644)
if err != nil {
panic(err)
}
Expand All @@ -92,13 +95,13 @@ func main() {
panic(err)
}

tcpFile, err := os.OpenFile(path.Join(destDir, "raw-ss-tcp"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
tcpFile, err := os.OpenFile(path.Join(destDir, "raw-ss-tcp"), os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
panic(err)
}
defer tcpFile.Close()

udpFile, err := os.OpenFile(path.Join(destDir, "raw-ss-udp"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
udpFile, err := os.OpenFile(path.Join(destDir, "raw-ss-udp"), os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
panic(err)
}
Expand All @@ -110,12 +113,35 @@ func main() {
}

nodesComDetails := []types.ComDetails{}

err = debug.CreateNamespace(cs, consts.DefaultDebugNamespace)
if err != nil {
panic(err)
}
defer func() {
err := debug.DeleteNamespace(cs, consts.DefaultDebugNamespace)
if err != nil {
panic(err)
}
}()

nLock := &sync.Mutex{}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: missing new line

g := new(errgroup.Group)
for _, n := range nodesList.Items {
node := n
g.Go(func() error {
cds, err := ss.CreateComDetailsFromNode(cs, &node, tcpFile, udpFile)
debugPod, err := debug.New(cs, node.Name, consts.DefaultDebugNamespace, consts.DefaultDebugPodImage)
if err != nil {
return err
}
defer func() {
err := debugPod.Clean()
if err != nil {
fmt.Printf("failed cleaning debug pod %s: %v", debugPod, err)
}
}()

cds, err := ss.CreateComDetailsFromNode(debugPod, &node, tcpFile, udpFile)
if err != nil {
return err
}
Expand All @@ -140,7 +166,7 @@ func main() {
}

ssMatrixFileName := filepath.Join(destDir, fmt.Sprintf("ss-generated-matrix.%s", format))
err = os.WriteFile(ssMatrixFileName, []byte(string(res)), 0644)
err = os.WriteFile(ssMatrixFileName, res, 0644)
if err != nil {
panic(err)
}
Expand Down
45 changes: 12 additions & 33 deletions commatrix/commatrix.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/openshift-kni/commatrix/types"
)

// TODO: add integration tests
// TODO: add integration tests.

type Env int

Expand Down Expand Up @@ -69,7 +69,9 @@ func New(kubeconfigPath string, customEntriesPath string, e Env, d Deployment) (
res = append(res, customComDetails...)
}

return &types.ComMatrix{Matrix: res}, nil
cleanedComDetails := types.RemoveDups(res)

return &types.ComMatrix{Matrix: cleanedComDetails}, nil
}

func addFromFile(fp string) ([]types.ComDetails, error) {
Expand All @@ -94,54 +96,31 @@ func addFromFile(fp string) ([]types.ComDetails, error) {

func getStaticEntries(e Env, d Deployment) ([]types.ComDetails, error) {
comDetails := []types.ComDetails{}
add := []types.ComDetails{}

switch e {
case Baremetal:
err := json.Unmarshal([]byte(baremetalStaticEntriesMaster), &add)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal static entries: %v", err)
}
comDetails = append(comDetails, add...)
comDetails = append(comDetails, baremetalStaticEntriesMaster...)
if d == SNO {
break
}
err = json.Unmarshal([]byte(baremetalStaticEntriesWorker), &add)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal static entries: %v", err)
}
comDetails = append(comDetails, add...)
comDetails = append(comDetails, baremetalStaticEntriesWorker...)
case AWS:
err := json.Unmarshal([]byte(awsCloudStaticEntriesMaster), &add)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal static entries: %v", err)
}
comDetails = append(comDetails, add...)
comDetails = append(comDetails, awsCloudStaticEntriesMaster...)
if d == SNO {
break
}
err = json.Unmarshal([]byte(awsCloudStaticEntriesWorker), &add)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal static entries: %v", err)
}
comDetails = append(comDetails, add...)
comDetails = append(comDetails, awsCloudStaticEntriesWorker...)
default:
return nil, fmt.Errorf("invalid value for cluster environment")
}

err := json.Unmarshal([]byte(generalStaticEntriesMaster), &add)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal static entries: %v", err)
}
comDetails = append(comDetails, add...)
comDetails = append(comDetails, generalStaticEntriesMaster...)
if d == SNO {
return comDetails, nil
}

err = json.Unmarshal([]byte(generalStaticEntriesWorker), &add)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal static entries: %v", err)
}
comDetails = append(comDetails, add...)
comDetails = append(comDetails, MNOStaticEntries...)
comDetails = append(comDetails, generalStaticEntriesWorker...)

return comDetails, nil
}
Loading
Loading