Skip to content

Commit

Permalink
Add FV test that verifies that the iptables lock blocks iptables.
Browse files Browse the repository at this point in the history
  • Loading branch information
fasaxc committed Jul 13, 2017
1 parent b2a2d91 commit 62b7638
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 1 deletion.
18 changes: 17 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ help:
.SUFFIXES:

all: deb rpm calico/felix
test: ut
test: ut fv

GO_BUILD_CONTAINER?=calico/go-build:v0.6

Expand Down Expand Up @@ -292,6 +292,12 @@ bin/calico-felix: $(FELIX_GO_FILES) vendor/.up-to-date
( ldd bin/calico-felix 2>&1 | grep -q "Not a valid dynamic program" || \
( echo "Error: bin/calico-felix was not statically linked"; false ) )'

bin/iptables-locker: $(FELIX_GO_FILES) vendor/.up-to-date
@echo Building iptables-locker...
mkdir -p bin
$(DOCKER_GO_BUILD) \
sh -c 'go build -v -i -o $@ -v $(LDFLAGS) "github.com/projectcalico/felix/fv/iptables-locker"'

bin/k8sfv.test: $(K8SFV_GO_FILES) vendor/.up-to-date
@echo Building $@...
$(DOCKER_GO_BUILD) \
Expand Down Expand Up @@ -324,6 +330,16 @@ ut combined.coverprofile: vendor/.up-to-date $(FELIX_GO_FILES)
@echo Running Go UTs.
$(DOCKER_GO_BUILD) ./utils/run-coverage

bin/fv.test: vendor/.up-to-date $(FELIX_GO_FILES)
$(DOCKER_GO_BUILD) go test ./fv -c --tags fvtests -o bin/fv.test

.PHONY: fv
fv: calico/felix bin/iptables-locker bin/fv.test
@echo Running Go FVs.
# For now, we pre-build the binary so that we can run it outside a container and allow it
# to interact with docker.
bin/fv.test

bin/check-licenses: $(FELIX_GO_FILES)
$(DOCKER_GO_BUILD) go build -v -i -o $@ "github.com/projectcalico/felix/check-licenses"

Expand Down
16 changes: 16 additions & 0 deletions fv/fv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) 2017 Tigera, Inc. All rights reserved.

// 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.

// The fv packge contains FV tests that execute Felix for-real.
package fv
35 changes: 35 additions & 0 deletions fv/fv_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2017 Tigera, Inc. All rights reserved.
//
// 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 fv_test

import (
"testing"

. "github.com/onsi/ginkgo"
"github.com/onsi/ginkgo/reporters"
. "github.com/onsi/gomega"

"github.com/projectcalico/libcalico-go/lib/testutils"
)

func init() {
testutils.HookLogrusForGinkgo()
}

func TestFv(t *testing.T) {
RegisterFailHandler(Fail)
junitReporter := reporters.NewJUnitReporter("junit.xml")
RunSpecsWithDefaultAndCustomReporters(t, "FV Suite", []Reporter{junitReporter})
}
55 changes: 55 additions & 0 deletions fv/iptables-locker/iptables-locker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) 2017 Tigera, Inc. All rights reserved.
//
// 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 main

import (
"time"

log "github.com/Sirupsen/logrus"
"github.com/docopt/docopt-go"

"github.com/projectcalico/felix/iptables"
)

const usage = `iptables-locker, test tool for grabbing the iptables lock.
Usage:
iptables-locker <duration>
`

func main() {
arguments, err := docopt.Parse(usage, nil, true, "v0.1", false)
if err != nil {
println(usage)
log.WithError(err).Fatal("Failed to parse usage")
}
durationStr := arguments["<duration>"].(string)
duration, err := time.ParseDuration(durationStr)
if err != nil {
println(usage)
log.WithError(err).Fatal("Failed to parse usage")
}

iptablesLock := iptables.NewSharedLock(
"/run/xtables.lock",
1*time.Second,
50*time.Millisecond,
)
iptablesLock.Lock()
println("LOCKED")
time.Sleep(duration)
iptablesLock.Unlock()
}
112 changes: 112 additions & 0 deletions fv/iptables_lock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// +build fvtests

// Copyright (c) 2017 Tigera, Inc. All rights reserved.
//
// 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 fv_test

import (
"bufio"
"fmt"
"os"
"os/exec"
"strings"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("with running container", func() {
var containerIdx int
var containerName string
var felixCmd *exec.Cmd

cmdInContainer := func(cmd ...string) *exec.Cmd {
arg := []string{"exec", containerName}
arg = append(arg, cmd...)
return exec.Command("docker", arg...)
}

BeforeEach(func() {
containerName = fmt.Sprintf("felix-fv-%d-%d", os.Getpid(), containerIdx)
containerIdx++
felixDir, err := os.Getwd()
Expect(err).NotTo(HaveOccurred())
// Run a felix container. The tests in this file don't actually rely on Felix
// but the calico/felix container has all the iptables dependencies we need to
// check the lock behaviour.
felixCmd = exec.Command("docker", "run",
"--rm",
"--name", containerName,
"-v", fmt.Sprintf("%s:/codebase", felixDir),
"--privileged",
"calico/felix")
err = felixCmd.Start()
Expect(err).NotTo(HaveOccurred())

for {
cmd := exec.Command("docker", "ps")
out, err := cmd.CombinedOutput()
Expect(err).NotTo(HaveOccurred())
if strings.Contains(string(out), containerName) {
break
}
}
})
AfterEach(func() {
// Send an interrupt to ensure that docker gracefully shuts down the container.
// If we kill the docker process then it detaches the container.
felixCmd.Process.Signal(os.Interrupt)
})

Describe("with the lock being held for 2s", func() {
var lockCmd *exec.Cmd
BeforeEach(func() {
// Start the iptables-locker, which is a simple test app that locks
// the iptables lock and then releases it after a timeout.
lockCmd = cmdInContainer("/codebase/bin/iptables-locker", "2s")
stdErr, err := lockCmd.StderrPipe()
Expect(err).NotTo(HaveOccurred())
lockCmd.Start()

// Wait for the iptables-locker to tell us that it actually acquired the
// lock.
scanner := bufio.NewScanner(stdErr)
Expect(scanner.Scan()).To(BeTrue())
Expect(scanner.Text()).To(Equal("LOCKED"))
Expect(scanner.Err()).NotTo(HaveOccurred())
})

It("iptables should fail to get the lock in 1s", func() {
iptCmd := cmdInContainer("iptables", "-w", "1", "-A", "FORWARD")
out, err := iptCmd.CombinedOutput()
Expect(string(out)).To(ContainSubstring("Stopped waiting"))
Expect(err).To(HaveOccurred())
})

It("iptables should succeed in getting the lock after 3s", func() {
iptCmd := cmdInContainer("iptables", "-w", "3", "-A", "FORWARD")
out, err := iptCmd.CombinedOutput()
Expect(string(out)).To(ContainSubstring("Another app is currently holding the xtables lock"))
Expect(err).NotTo(HaveOccurred())
})

AfterEach(func() {
if lockCmd != nil {
err := lockCmd.Wait()
Expect(err).NotTo(HaveOccurred())
}
})
})
})

0 comments on commit 62b7638

Please sign in to comment.