Skip to content

Commit

Permalink
Merge pull request #1 from denis-tingaikin/implenet-initial-version-o…
Browse files Browse the repository at this point in the history
…f-cmd-map-ip-k8s

Implement initial version of cmd-map-ip-k8s
  • Loading branch information
edwarnicke authored May 31, 2021
2 parents bf1049e + f651166 commit eba529d
Show file tree
Hide file tree
Showing 8 changed files with 983 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ RUN dl https://github.com/spiffe/spire/releases/download/v0.11.1/spire-0.11.1-li

FROM go as build
WORKDIR /build
COPY go.mod go.sum ./
COPY ./internal/imports imports
RUN go build ./imports
COPY . .
RUN go build -o /bin/app .

Expand Down
16 changes: 15 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
module github.com/networkservicemesh/cmd-template
module github.com/networkservicemesh/cmd-map-ip-k8s

go 1.16

require (
github.com/antonfisher/nested-logrus-formatter v1.3.1
github.com/edwarnicke/serialize v1.0.7
github.com/kelseyhightower/envconfig v1.4.0
github.com/networkservicemesh/sdk v0.0.0-20210531072719-eaf26022ad56
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.0
go.uber.org/goleak v1.1.10
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.21.1
k8s.io/apimachinery v0.21.1
k8s.io/client-go v0.21.1
)
578 changes: 578 additions & 0 deletions go.sum

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions internal/imports/gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) 2021 Cisco and/or its affiliates.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package imports is used for generating list of imports to optimize use of docker build cache
package imports

//go:generate bash -c "rm -rf imports_linux.go"
//go:generate bash -c "cd $(mktemp -d) && GO111MODULE=on go get github.com/edwarnicke/imports-gen@v1.1.0"
//go:generate bash -c "GOOS=linux ${GOPATH}/bin/imports-gen"
29 changes: 29 additions & 0 deletions internal/imports/imports_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// DO NOT EDIT - generated by github.com/edwarnicke/imports-gen
package imports

import (
_ "context"
_ "github.com/antonfisher/nested-logrus-formatter"
_ "github.com/edwarnicke/serialize"
_ "github.com/kelseyhightower/envconfig"
_ "github.com/networkservicemesh/sdk/pkg/tools/log"
_ "github.com/networkservicemesh/sdk/pkg/tools/log/logruslogger"
_ "github.com/sirupsen/logrus"
_ "github.com/stretchr/testify/require"
_ "go.uber.org/goleak"
_ "gopkg.in/yaml.v2"
_ "io/ioutil"
_ "k8s.io/api/core/v1"
_ "k8s.io/apimachinery/pkg/apis/meta/v1"
_ "k8s.io/apimachinery/pkg/watch"
_ "k8s.io/client-go/kubernetes"
_ "k8s.io/client-go/kubernetes/fake"
_ "k8s.io/client-go/rest"
_ "os"
_ "os/signal"
_ "path/filepath"
_ "strings"
_ "syscall"
_ "testing"
_ "time"
)
119 changes: 119 additions & 0 deletions internal/mapipwriter/mapipwriter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright (c) 2021 Doc.ai and/or its affiliates.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package mapipwriter provides MapIPWriter struct that can handle events related to v1.Node
package mapipwriter

import (
"context"
"io/ioutil"
"os"
"path/filepath"

"github.com/edwarnicke/serialize"
"gopkg.in/yaml.v2"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/watch"

"github.com/networkservicemesh/sdk/pkg/tools/log"
)

// MapIPWriter writes IPs from the v1.Node into OutputPath
type MapIPWriter struct {
OutputPath string
exec serialize.Executor
internalToExternalIP map[string]string
}

func (m *MapIPWriter) writeToFile(ctx context.Context) {
_ = os.MkdirAll(filepath.Dir(m.OutputPath), os.ModePerm)

bytes, err := yaml.Marshal(m.internalToExternalIP)

if err != nil {
log.FromContext(ctx).Errorf("an error during marshaling ips map: %v, err: %v", m.OutputPath, err.Error())
return
}

err = ioutil.WriteFile(m.OutputPath, bytes, os.ModePerm)

if err != nil {
log.FromContext(ctx).Errorf("an error during marshaling ips map: %v, err: %v", m.OutputPath, err.Error())
}
}

// Start starts reading events from the passed channel in the current goroutine
func (m *MapIPWriter) Start(ctx context.Context, eventCh <-chan watch.Event) {
for {
select {
case <-ctx.Done():
return
case event, ok := <-eventCh:
if !ok {
continue
}
node, ok := event.Object.(*v1.Node)
if !ok {
continue
}
if node == nil {
continue
}

internalIP, externalIP := getAddress(node.Status.Addresses, v1.NodeInternalIP), getAddress(node.Status.Addresses, v1.NodeExternalIP)

if internalIP == "" && externalIP == "" {
continue
}

if internalIP == "" {
internalIP = externalIP
}

if externalIP == "" {
externalIP = internalIP
}

eventType := event.Type

m.exec.AsyncExec(func() {
if m.internalToExternalIP == nil {
m.internalToExternalIP = map[string]string{}
}
switch eventType {
case watch.Deleted:
delete(m.internalToExternalIP, internalIP)
default:
m.internalToExternalIP[internalIP] = externalIP
}
m.exec.AsyncExec(func() {
m.writeToFile(ctx)
})
})
}
}
}

func getAddress(addresses []v1.NodeAddress, addressType v1.NodeAddressType) string {
for i := 0; i < len(addresses); i++ {
addr := &addresses[i]
if addr.Type == addressType {
return addr.Address
}
}

return ""
}
114 changes: 114 additions & 0 deletions internal/mapipwriter/mapipwriter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright (c) 2021 Doc.ai and/or its affiliates.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package mapipwriter_test

import (
"context"
"io/ioutil"
"path/filepath"
"strings"
"testing"
"time"

"github.com/stretchr/testify/require"
"go.uber.org/goleak"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"

"github.com/networkservicemesh/cmd-map-ip-k8s/internal/mapipwriter"
)

func Test_MapWriter(t *testing.T) {
defer goleak.VerifyNone(t, goleak.IgnoreCurrent())

outputFile := filepath.Join(t.TempDir(), "output.yaml")

ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()

var writer = mapipwriter.MapIPWriter{
OutputPath: outputFile,
}

cleintset := fake.NewSimpleClientset()

w, err := cleintset.CoreV1().Nodes().Watch(ctx, metav1.ListOptions{})
require.NoError(t, err)

defer w.Stop()

go writer.Start(ctx, w.ResultChan())

_, err = cleintset.CoreV1().Nodes().Create(ctx, &v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node-controlplane",
},
Status: v1.NodeStatus{

Addresses: []v1.NodeAddress{
{
Type: v1.NodeExternalIP,
Address: "148.142.120.1",
},
{
Type: v1.NodeInternalIP,
Address: "127.0.0.1",
},
},
},
}, metav1.CreateOptions{})
require.NoError(t, err)

_, err = cleintset.CoreV1().Nodes().Create(ctx, &v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node-worker-1",
},
Status: v1.NodeStatus{
Addresses: []v1.NodeAddress{
{
Type: v1.NodeInternalIP,
Address: "1.1.1.1",
},
},
},
}, metav1.CreateOptions{})
require.NoError(t, err)

require.Eventually(t, func() bool {
// #nosec
b, readErr := ioutil.ReadFile(outputFile)
if readErr != nil {
return false
}
s := string(b)
return strings.Contains(s, "127.0.0.1: 148.142.120.1") && strings.Contains(s, "1.1.1.1: 1.1.1.1")
}, time.Second, time.Millisecond*100)

err = cleintset.CoreV1().Nodes().Delete(ctx, "node-worker-1", metav1.DeleteOptions{})
require.NoError(t, err)

require.Eventually(t, func() bool {
// #nosec
b, readErr := ioutil.ReadFile(outputFile)
if readErr != nil {
return false
}
s := strings.TrimSpace(string(b))
return s == "127.0.0.1: 148.142.120.1"
}, time.Second, time.Millisecond*100)
}
Loading

0 comments on commit eba529d

Please sign in to comment.