Decouple shared functions between controllers (#8829)
* Decouple shared functions between controllers * Apply suggestions from code review Co-authored-by: Jintao Zhang <tao12345666333@163.com> * Fix package names and fmt Co-authored-by: Jintao Zhang <tao12345666333@163.com>
This commit is contained in:
parent
8f9df544ea
commit
4c6a7ee158
27 changed files with 413 additions and 134 deletions
38
pkg/util/file/file.go
Normal file
38
pkg/util/file/file.go
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
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 file
|
||||
|
||||
import (
|
||||
"crypto/sha1" // #nosec
|
||||
"encoding/hex"
|
||||
"os"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// SHA1 returns the SHA1 of a file.
|
||||
func SHA1(filename string) string {
|
||||
hasher := sha1.New() // #nosec
|
||||
s, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "Error reading file", "path", filename)
|
||||
return ""
|
||||
}
|
||||
|
||||
hasher.Write(s)
|
||||
return hex.EncodeToString(hasher.Sum(nil))
|
||||
}
|
||||
53
pkg/util/file/file_test.go
Normal file
53
pkg/util/file/file_test.go
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
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 file
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSHA1(t *testing.T) {
|
||||
tests := []struct {
|
||||
content []byte
|
||||
sha string
|
||||
}{
|
||||
{[]byte(""), "da39a3ee5e6b4b0d3255bfef95601890afd80709"},
|
||||
{[]byte("hello world"), "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
f, err := os.CreateTemp("", "sha-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.Write(test.content)
|
||||
f.Sync()
|
||||
|
||||
sha := SHA1(f.Name())
|
||||
f.Close()
|
||||
|
||||
if sha != test.sha {
|
||||
t.Fatalf("expected %v but returned %s", test.sha, sha)
|
||||
}
|
||||
}
|
||||
|
||||
sha := SHA1("")
|
||||
if sha != "" {
|
||||
t.Fatalf("expected an empty sha but returned %s", sha)
|
||||
}
|
||||
}
|
||||
20
pkg/util/file/filesystem.go
Normal file
20
pkg/util/file/filesystem.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
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 file
|
||||
|
||||
// ReadWriteByUser defines linux permission to read and write files for the owner user
|
||||
const ReadWriteByUser = 0700
|
||||
63
pkg/util/file/structure.go
Normal file
63
pkg/util/file/structure.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
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 file
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
// AuthDirectory default directory used to store files
|
||||
// to authenticate request
|
||||
AuthDirectory = "/etc/ingress-controller/auth"
|
||||
|
||||
// DefaultSSLDirectory defines the location where the SSL certificates will be generated
|
||||
// This directory contains all the SSL certificates that are specified in Ingress rules.
|
||||
// The name of each file is <namespace>-<secret name>.pem. The content is the concatenated
|
||||
// certificate and key.
|
||||
DefaultSSLDirectory = "/etc/ingress-controller/ssl"
|
||||
)
|
||||
|
||||
var (
|
||||
directories = []string{
|
||||
DefaultSSLDirectory,
|
||||
AuthDirectory,
|
||||
}
|
||||
)
|
||||
|
||||
// CreateRequiredDirectories verifies if the required directories to
|
||||
// start the ingress controller exist and creates the missing ones.
|
||||
func CreateRequiredDirectories() error {
|
||||
for _, directory := range directories {
|
||||
_, err := os.Stat(directory)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(directory, ReadWriteByUser)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating directory %s: %w", directory, err)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
return fmt.Errorf("checking directory %s: %w", directory, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
24
pkg/util/process/controller.go
Normal file
24
pkg/util/process/controller.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
Copyright 2022 The Kubernetes Authors.
|
||||
|
||||
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 process
|
||||
|
||||
// ProcessController defines a common interface for a process to be controlled,
|
||||
// like the configurer, the webhook or the proper ingress controller
|
||||
type ProcessController interface {
|
||||
Start()
|
||||
Stop() error
|
||||
}
|
||||
49
pkg/util/process/sigterm.go
Normal file
49
pkg/util/process/sigterm.go
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
Copyright 2022 The Kubernetes Authors.
|
||||
|
||||
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 process
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
klog "k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
type exiter func(code int)
|
||||
|
||||
// HandleSigterm receives a ProcessController interface and deals with
|
||||
// the graceful shutdown
|
||||
func HandleSigterm(ngx ProcessController, delay int, exit exiter) {
|
||||
signalChan := make(chan os.Signal, 1)
|
||||
signal.Notify(signalChan, syscall.SIGTERM)
|
||||
<-signalChan
|
||||
klog.InfoS("Received SIGTERM, shutting down")
|
||||
|
||||
exitCode := 0
|
||||
if err := ngx.Stop(); err != nil {
|
||||
klog.Warningf("Error during shutdown: %v", err)
|
||||
exitCode = 1
|
||||
}
|
||||
|
||||
klog.Infof("Handled quit, delaying controller exit for %d seconds", delay)
|
||||
time.Sleep(time.Duration(delay) * time.Second)
|
||||
|
||||
klog.InfoS("Exiting", "code", exitCode)
|
||||
exit(exitCode)
|
||||
}
|
||||
79
pkg/util/process/sigterm_test.go
Normal file
79
pkg/util/process/sigterm_test.go
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
Copyright 2022 The Kubernetes Authors.
|
||||
|
||||
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 process
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FakeProcess struct {
|
||||
shouldError bool
|
||||
exitCode int
|
||||
}
|
||||
|
||||
func (f *FakeProcess) Start() {
|
||||
}
|
||||
|
||||
func (f *FakeProcess) Stop() error {
|
||||
if f.shouldError {
|
||||
return fmt.Errorf("error")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeProcess) exiterFunc(code int) {
|
||||
f.exitCode = code
|
||||
}
|
||||
|
||||
func sendDelayedSignal(delay time.Duration) {
|
||||
time.Sleep(delay * time.Second)
|
||||
syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
|
||||
}
|
||||
|
||||
func TestHandleSigterm(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
shouldError bool
|
||||
delay int
|
||||
}{
|
||||
{
|
||||
name: "should exit without error",
|
||||
shouldError: false,
|
||||
},
|
||||
{
|
||||
name: "should exit with error",
|
||||
shouldError: true,
|
||||
delay: 2,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
process := &FakeProcess{shouldError: tt.shouldError}
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
go sendDelayedSignal(2) // Send a signal after 2 seconds
|
||||
HandleSigterm(process, tt.delay, process.exiterFunc)
|
||||
})
|
||||
if tt.shouldError && process.exitCode != 1 {
|
||||
t.Errorf("wrong return, should be 1 and returned %d", process.exitCode)
|
||||
}
|
||||
if !tt.shouldError && process.exitCode != 0 {
|
||||
t.Errorf("wrong return, should be 0 and returned %d", process.exitCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
67
pkg/util/runtime/cpu_linux.go
Normal file
67
pkg/util/runtime/cpu_linux.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
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 runtime
|
||||
|
||||
import (
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
libcontainercgroups "github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
)
|
||||
|
||||
// NumCPU returns the number of logical CPUs usable by the current process.
|
||||
// If CPU cgroups limits are configured, use cfs_quota_us / cfs_period_us
|
||||
// as formula
|
||||
// https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
|
||||
func NumCPU() int {
|
||||
cpus := runtime.NumCPU()
|
||||
|
||||
cgroupPath, err := libcontainercgroups.FindCgroupMountpoint("", "cpu")
|
||||
if err != nil {
|
||||
return cpus
|
||||
}
|
||||
|
||||
cpuQuota := readCgroupFileToInt64(cgroupPath, "cpu.cfs_quota_us")
|
||||
cpuPeriod := readCgroupFileToInt64(cgroupPath, "cpu.cfs_period_us")
|
||||
|
||||
if cpuQuota == -1 || cpuPeriod == -1 {
|
||||
return cpus
|
||||
}
|
||||
|
||||
return int(math.Ceil(float64(cpuQuota) / float64(cpuPeriod)))
|
||||
}
|
||||
|
||||
func readCgroupFileToInt64(cgroupPath, cgroupFile string) int64 {
|
||||
contents, err := os.ReadFile(filepath.Join(cgroupPath, cgroupFile))
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
strValue := strings.TrimSpace(string(contents))
|
||||
if value, err := strconv.ParseInt(strValue, 10, 64); err == nil {
|
||||
return value
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
29
pkg/util/runtime/cpu_notlinux.go
Normal file
29
pkg/util/runtime/cpu_notlinux.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
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 runtime
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// NumCPU ...
|
||||
func NumCPU() int {
|
||||
return runtime.NumCPU()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue