Skip to content

Commit

Permalink
Merge pull request #4 from liornoy/fix-open-issues
Browse files Browse the repository at this point in the history
Fix open issues
  • Loading branch information
sabinaaledort authored May 21, 2024
2 parents ee48bff + 39d8f26 commit fb1b862
Show file tree
Hide file tree
Showing 11 changed files with 733 additions and 777 deletions.
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

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:
```
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)
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{}
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

0 comments on commit fb1b862

Please sign in to comment.