Remove the unused pkgs
We're not using pkg/jsonmessage, pkg/parsers/operatingsystem, pkg/pidfile, or pkg/sysinfo, so remove them. Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
This commit is contained in:
parent
55e5d76085
commit
c4bcd1cf17
21 changed files with 0 additions and 1719 deletions
|
@ -1,221 +0,0 @@
|
||||||
package jsonmessage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/containers/storage/pkg/jsonlog"
|
|
||||||
"github.com/containers/storage/pkg/term"
|
|
||||||
"github.com/docker/go-units"
|
|
||||||
)
|
|
||||||
|
|
||||||
// JSONError wraps a concrete Code and Message, `Code` is
|
|
||||||
// is an integer error code, `Message` is the error message.
|
|
||||||
type JSONError struct {
|
|
||||||
Code int `json:"code,omitempty"`
|
|
||||||
Message string `json:"message,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *JSONError) Error() string {
|
|
||||||
return e.Message
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSONProgress describes a Progress. terminalFd is the fd of the current terminal,
|
|
||||||
// Start is the initial value for the operation. Current is the current status and
|
|
||||||
// value of the progress made towards Total. Total is the end value describing when
|
|
||||||
// we made 100% progress for an operation.
|
|
||||||
type JSONProgress struct {
|
|
||||||
terminalFd uintptr
|
|
||||||
Current int64 `json:"current,omitempty"`
|
|
||||||
Total int64 `json:"total,omitempty"`
|
|
||||||
Start int64 `json:"start,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *JSONProgress) String() string {
|
|
||||||
var (
|
|
||||||
width = 200
|
|
||||||
pbBox string
|
|
||||||
numbersBox string
|
|
||||||
timeLeftBox string
|
|
||||||
)
|
|
||||||
|
|
||||||
ws, err := term.GetWinsize(p.terminalFd)
|
|
||||||
if err == nil {
|
|
||||||
width = int(ws.Width)
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.Current <= 0 && p.Total <= 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
current := units.HumanSize(float64(p.Current))
|
|
||||||
if p.Total <= 0 {
|
|
||||||
return fmt.Sprintf("%8v", current)
|
|
||||||
}
|
|
||||||
total := units.HumanSize(float64(p.Total))
|
|
||||||
percentage := int(float64(p.Current)/float64(p.Total)*100) / 2
|
|
||||||
if percentage > 50 {
|
|
||||||
percentage = 50
|
|
||||||
}
|
|
||||||
if width > 110 {
|
|
||||||
// this number can't be negative gh#7136
|
|
||||||
numSpaces := 0
|
|
||||||
if 50-percentage > 0 {
|
|
||||||
numSpaces = 50 - percentage
|
|
||||||
}
|
|
||||||
pbBox = fmt.Sprintf("[%s>%s] ", strings.Repeat("=", percentage), strings.Repeat(" ", numSpaces))
|
|
||||||
}
|
|
||||||
|
|
||||||
numbersBox = fmt.Sprintf("%8v/%v", current, total)
|
|
||||||
|
|
||||||
if p.Current > p.Total {
|
|
||||||
// remove total display if the reported current is wonky.
|
|
||||||
numbersBox = fmt.Sprintf("%8v", current)
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.Current > 0 && p.Start > 0 && percentage < 50 {
|
|
||||||
fromStart := time.Now().UTC().Sub(time.Unix(p.Start, 0))
|
|
||||||
perEntry := fromStart / time.Duration(p.Current)
|
|
||||||
left := time.Duration(p.Total-p.Current) * perEntry
|
|
||||||
left = (left / time.Second) * time.Second
|
|
||||||
|
|
||||||
if width > 50 {
|
|
||||||
timeLeftBox = " " + left.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pbBox + numbersBox + timeLeftBox
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSONMessage defines a message struct. It describes
|
|
||||||
// the created time, where it from, status, ID of the
|
|
||||||
// message. It's used for docker events.
|
|
||||||
type JSONMessage struct {
|
|
||||||
Stream string `json:"stream,omitempty"`
|
|
||||||
Status string `json:"status,omitempty"`
|
|
||||||
Progress *JSONProgress `json:"progressDetail,omitempty"`
|
|
||||||
ProgressMessage string `json:"progress,omitempty"` //deprecated
|
|
||||||
ID string `json:"id,omitempty"`
|
|
||||||
From string `json:"from,omitempty"`
|
|
||||||
Time int64 `json:"time,omitempty"`
|
|
||||||
TimeNano int64 `json:"timeNano,omitempty"`
|
|
||||||
Error *JSONError `json:"errorDetail,omitempty"`
|
|
||||||
ErrorMessage string `json:"error,omitempty"` //deprecated
|
|
||||||
// Aux contains out-of-band data, such as digests for push signing.
|
|
||||||
Aux *json.RawMessage `json:"aux,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display displays the JSONMessage to `out`. `isTerminal` describes if `out`
|
|
||||||
// is a terminal. If this is the case, it will erase the entire current line
|
|
||||||
// when displaying the progressbar.
|
|
||||||
func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error {
|
|
||||||
if jm.Error != nil {
|
|
||||||
if jm.Error.Code == 401 {
|
|
||||||
return fmt.Errorf("Authentication is required.")
|
|
||||||
}
|
|
||||||
return jm.Error
|
|
||||||
}
|
|
||||||
var endl string
|
|
||||||
if isTerminal && jm.Stream == "" && jm.Progress != nil {
|
|
||||||
// <ESC>[2K = erase entire current line
|
|
||||||
fmt.Fprintf(out, "%c[2K\r", 27)
|
|
||||||
endl = "\r"
|
|
||||||
} else if jm.Progress != nil && jm.Progress.String() != "" { //disable progressbar in non-terminal
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if jm.TimeNano != 0 {
|
|
||||||
fmt.Fprintf(out, "%s ", time.Unix(0, jm.TimeNano).Format(jsonlog.RFC3339NanoFixed))
|
|
||||||
} else if jm.Time != 0 {
|
|
||||||
fmt.Fprintf(out, "%s ", time.Unix(jm.Time, 0).Format(jsonlog.RFC3339NanoFixed))
|
|
||||||
}
|
|
||||||
if jm.ID != "" {
|
|
||||||
fmt.Fprintf(out, "%s: ", jm.ID)
|
|
||||||
}
|
|
||||||
if jm.From != "" {
|
|
||||||
fmt.Fprintf(out, "(from %s) ", jm.From)
|
|
||||||
}
|
|
||||||
if jm.Progress != nil && isTerminal {
|
|
||||||
fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress.String(), endl)
|
|
||||||
} else if jm.ProgressMessage != "" { //deprecated
|
|
||||||
fmt.Fprintf(out, "%s %s%s", jm.Status, jm.ProgressMessage, endl)
|
|
||||||
} else if jm.Stream != "" {
|
|
||||||
fmt.Fprintf(out, "%s%s", jm.Stream, endl)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(out, "%s%s\n", jm.Status, endl)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisplayJSONMessagesStream displays a json message stream from `in` to `out`, `isTerminal`
|
|
||||||
// describes if `out` is a terminal. If this is the case, it will print `\n` at the end of
|
|
||||||
// each line and move the cursor while displaying.
|
|
||||||
func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(*json.RawMessage)) error {
|
|
||||||
var (
|
|
||||||
dec = json.NewDecoder(in)
|
|
||||||
ids = make(map[string]int)
|
|
||||||
)
|
|
||||||
for {
|
|
||||||
diff := 0
|
|
||||||
var jm JSONMessage
|
|
||||||
if err := dec.Decode(&jm); err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if jm.Aux != nil {
|
|
||||||
if auxCallback != nil {
|
|
||||||
auxCallback(jm.Aux)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if jm.Progress != nil {
|
|
||||||
jm.Progress.terminalFd = terminalFd
|
|
||||||
}
|
|
||||||
if jm.ID != "" && (jm.Progress != nil || jm.ProgressMessage != "") {
|
|
||||||
line, ok := ids[jm.ID]
|
|
||||||
if !ok {
|
|
||||||
// NOTE: This approach of using len(id) to
|
|
||||||
// figure out the number of lines of history
|
|
||||||
// only works as long as we clear the history
|
|
||||||
// when we output something that's not
|
|
||||||
// accounted for in the map, such as a line
|
|
||||||
// with no ID.
|
|
||||||
line = len(ids)
|
|
||||||
ids[jm.ID] = line
|
|
||||||
if isTerminal {
|
|
||||||
fmt.Fprintf(out, "\n")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
diff = len(ids) - line
|
|
||||||
}
|
|
||||||
if isTerminal {
|
|
||||||
// NOTE: this appears to be necessary even if
|
|
||||||
// diff == 0.
|
|
||||||
// <ESC>[{diff}A = move cursor up diff rows
|
|
||||||
fmt.Fprintf(out, "%c[%dA", 27, diff)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// When outputting something that isn't progress
|
|
||||||
// output, clear the history of previous lines. We
|
|
||||||
// don't want progress entries from some previous
|
|
||||||
// operation to be updated (for example, pull -a
|
|
||||||
// with multiple tags).
|
|
||||||
ids = make(map[string]int)
|
|
||||||
}
|
|
||||||
err := jm.Display(out, isTerminal)
|
|
||||||
if jm.ID != "" && isTerminal {
|
|
||||||
// NOTE: this appears to be necessary even if
|
|
||||||
// diff == 0.
|
|
||||||
// <ESC>[{diff}B = move cursor down diff rows
|
|
||||||
fmt.Fprintf(out, "%c[%dB", 27, diff)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,245 +0,0 @@
|
||||||
package jsonmessage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/containers/storage/pkg/jsonlog"
|
|
||||||
"github.com/containers/storage/pkg/term"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestError(t *testing.T) {
|
|
||||||
je := JSONError{404, "Not found"}
|
|
||||||
if je.Error() != "Not found" {
|
|
||||||
t.Fatalf("Expected 'Not found' got '%s'", je.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProgress(t *testing.T) {
|
|
||||||
termsz, err := term.GetWinsize(0)
|
|
||||||
if err != nil {
|
|
||||||
// we can safely ignore the err here
|
|
||||||
termsz = nil
|
|
||||||
}
|
|
||||||
jp := JSONProgress{}
|
|
||||||
if jp.String() != "" {
|
|
||||||
t.Fatalf("Expected empty string, got '%s'", jp.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := " 1 B"
|
|
||||||
jp2 := JSONProgress{Current: 1}
|
|
||||||
if jp2.String() != expected {
|
|
||||||
t.Fatalf("Expected %q, got %q", expected, jp2.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedStart := "[==========> ] 20 B/100 B"
|
|
||||||
if termsz != nil && termsz.Width <= 110 {
|
|
||||||
expectedStart = " 20 B/100 B"
|
|
||||||
}
|
|
||||||
jp3 := JSONProgress{Current: 20, Total: 100, Start: time.Now().Unix()}
|
|
||||||
// Just look at the start of the string
|
|
||||||
// (the remaining time is really hard to test -_-)
|
|
||||||
if jp3.String()[:len(expectedStart)] != expectedStart {
|
|
||||||
t.Fatalf("Expected to start with %q, got %q", expectedStart, jp3.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
expected = "[=========================> ] 50 B/100 B"
|
|
||||||
if termsz != nil && termsz.Width <= 110 {
|
|
||||||
expected = " 50 B/100 B"
|
|
||||||
}
|
|
||||||
jp4 := JSONProgress{Current: 50, Total: 100}
|
|
||||||
if jp4.String() != expected {
|
|
||||||
t.Fatalf("Expected %q, got %q", expected, jp4.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// this number can't be negative gh#7136
|
|
||||||
expected = "[==================================================>] 50 B"
|
|
||||||
if termsz != nil && termsz.Width <= 110 {
|
|
||||||
expected = " 50 B"
|
|
||||||
}
|
|
||||||
jp5 := JSONProgress{Current: 50, Total: 40}
|
|
||||||
if jp5.String() != expected {
|
|
||||||
t.Fatalf("Expected %q, got %q", expected, jp5.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestJSONMessageDisplay(t *testing.T) {
|
|
||||||
now := time.Now()
|
|
||||||
messages := map[JSONMessage][]string{
|
|
||||||
// Empty
|
|
||||||
JSONMessage{}: {"\n", "\n"},
|
|
||||||
// Status
|
|
||||||
JSONMessage{
|
|
||||||
Status: "status",
|
|
||||||
}: {
|
|
||||||
"status\n",
|
|
||||||
"status\n",
|
|
||||||
},
|
|
||||||
// General
|
|
||||||
JSONMessage{
|
|
||||||
Time: now.Unix(),
|
|
||||||
ID: "ID",
|
|
||||||
From: "From",
|
|
||||||
Status: "status",
|
|
||||||
}: {
|
|
||||||
fmt.Sprintf("%v ID: (from From) status\n", time.Unix(now.Unix(), 0).Format(jsonlog.RFC3339NanoFixed)),
|
|
||||||
fmt.Sprintf("%v ID: (from From) status\n", time.Unix(now.Unix(), 0).Format(jsonlog.RFC3339NanoFixed)),
|
|
||||||
},
|
|
||||||
// General, with nano precision time
|
|
||||||
JSONMessage{
|
|
||||||
TimeNano: now.UnixNano(),
|
|
||||||
ID: "ID",
|
|
||||||
From: "From",
|
|
||||||
Status: "status",
|
|
||||||
}: {
|
|
||||||
fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
|
|
||||||
fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
|
|
||||||
},
|
|
||||||
// General, with both times Nano is preferred
|
|
||||||
JSONMessage{
|
|
||||||
Time: now.Unix(),
|
|
||||||
TimeNano: now.UnixNano(),
|
|
||||||
ID: "ID",
|
|
||||||
From: "From",
|
|
||||||
Status: "status",
|
|
||||||
}: {
|
|
||||||
fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
|
|
||||||
fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
|
|
||||||
},
|
|
||||||
// Stream over status
|
|
||||||
JSONMessage{
|
|
||||||
Status: "status",
|
|
||||||
Stream: "stream",
|
|
||||||
}: {
|
|
||||||
"stream",
|
|
||||||
"stream",
|
|
||||||
},
|
|
||||||
// With progress message
|
|
||||||
JSONMessage{
|
|
||||||
Status: "status",
|
|
||||||
ProgressMessage: "progressMessage",
|
|
||||||
}: {
|
|
||||||
"status progressMessage",
|
|
||||||
"status progressMessage",
|
|
||||||
},
|
|
||||||
// With progress, stream empty
|
|
||||||
JSONMessage{
|
|
||||||
Status: "status",
|
|
||||||
Stream: "",
|
|
||||||
Progress: &JSONProgress{Current: 1},
|
|
||||||
}: {
|
|
||||||
"",
|
|
||||||
fmt.Sprintf("%c[2K\rstatus 1 B\r", 27),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// The tests :)
|
|
||||||
for jsonMessage, expectedMessages := range messages {
|
|
||||||
// Without terminal
|
|
||||||
data := bytes.NewBuffer([]byte{})
|
|
||||||
if err := jsonMessage.Display(data, false); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if data.String() != expectedMessages[0] {
|
|
||||||
t.Fatalf("Expected [%v], got [%v]", expectedMessages[0], data.String())
|
|
||||||
}
|
|
||||||
// With terminal
|
|
||||||
data = bytes.NewBuffer([]byte{})
|
|
||||||
if err := jsonMessage.Display(data, true); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if data.String() != expectedMessages[1] {
|
|
||||||
t.Fatalf("Expected [%v], got [%v]", expectedMessages[1], data.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test JSONMessage with an Error. It will return an error with the text as error, not the meaning of the HTTP code.
|
|
||||||
func TestJSONMessageDisplayWithJSONError(t *testing.T) {
|
|
||||||
data := bytes.NewBuffer([]byte{})
|
|
||||||
jsonMessage := JSONMessage{Error: &JSONError{404, "Can't find it"}}
|
|
||||||
|
|
||||||
err := jsonMessage.Display(data, true)
|
|
||||||
if err == nil || err.Error() != "Can't find it" {
|
|
||||||
t.Fatalf("Expected a JSONError 404, got [%v]", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonMessage = JSONMessage{Error: &JSONError{401, "Anything"}}
|
|
||||||
err = jsonMessage.Display(data, true)
|
|
||||||
if err == nil || err.Error() != "Authentication is required." {
|
|
||||||
t.Fatalf("Expected an error [Authentication is required.], got [%v]", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDisplayJSONMessagesStreamInvalidJSON(t *testing.T) {
|
|
||||||
var (
|
|
||||||
inFd uintptr
|
|
||||||
)
|
|
||||||
data := bytes.NewBuffer([]byte{})
|
|
||||||
reader := strings.NewReader("This is not a 'valid' JSON []")
|
|
||||||
inFd, _ = term.GetFdInfo(reader)
|
|
||||||
|
|
||||||
if err := DisplayJSONMessagesStream(reader, data, inFd, false, nil); err == nil && err.Error()[:17] != "invalid character" {
|
|
||||||
t.Fatalf("Should have thrown an error (invalid character in ..), got [%v]", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDisplayJSONMessagesStream(t *testing.T) {
|
|
||||||
var (
|
|
||||||
inFd uintptr
|
|
||||||
)
|
|
||||||
|
|
||||||
messages := map[string][]string{
|
|
||||||
// empty string
|
|
||||||
"": {
|
|
||||||
"",
|
|
||||||
""},
|
|
||||||
// Without progress & ID
|
|
||||||
"{ \"status\": \"status\" }": {
|
|
||||||
"status\n",
|
|
||||||
"status\n",
|
|
||||||
},
|
|
||||||
// Without progress, with ID
|
|
||||||
"{ \"id\": \"ID\",\"status\": \"status\" }": {
|
|
||||||
"ID: status\n",
|
|
||||||
fmt.Sprintf("ID: status\n%c[%dB", 27, 0),
|
|
||||||
},
|
|
||||||
// With progress
|
|
||||||
"{ \"id\": \"ID\", \"status\": \"status\", \"progress\": \"ProgressMessage\" }": {
|
|
||||||
"ID: status ProgressMessage",
|
|
||||||
fmt.Sprintf("\n%c[%dAID: status ProgressMessage%c[%dB", 27, 0, 27, 0),
|
|
||||||
},
|
|
||||||
// With progressDetail
|
|
||||||
"{ \"id\": \"ID\", \"status\": \"status\", \"progressDetail\": { \"Current\": 1} }": {
|
|
||||||
"", // progressbar is disabled in non-terminal
|
|
||||||
fmt.Sprintf("\n%c[%dA%c[2K\rID: status 1 B\r%c[%dB", 27, 0, 27, 27, 0),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for jsonMessage, expectedMessages := range messages {
|
|
||||||
data := bytes.NewBuffer([]byte{})
|
|
||||||
reader := strings.NewReader(jsonMessage)
|
|
||||||
inFd, _ = term.GetFdInfo(reader)
|
|
||||||
|
|
||||||
// Without terminal
|
|
||||||
if err := DisplayJSONMessagesStream(reader, data, inFd, false, nil); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if data.String() != expectedMessages[0] {
|
|
||||||
t.Fatalf("Expected an [%v], got [%v]", expectedMessages[0], data.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// With terminal
|
|
||||||
data = bytes.NewBuffer([]byte{})
|
|
||||||
reader = strings.NewReader(jsonMessage)
|
|
||||||
if err := DisplayJSONMessagesStream(reader, data, inFd, true, nil); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if data.String() != expectedMessages[1] {
|
|
||||||
t.Fatalf("Expected an [%v], got [%v]", expectedMessages[1], data.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
// Package operatingsystem provides helper function to get the operating system
|
|
||||||
// name for different platforms.
|
|
||||||
package operatingsystem
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/mattn/go-shellwords"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// file to use to detect if the daemon is running in a container
|
|
||||||
proc1Cgroup = "/proc/1/cgroup"
|
|
||||||
|
|
||||||
// file to check to determine Operating System
|
|
||||||
etcOsRelease = "/etc/os-release"
|
|
||||||
|
|
||||||
// used by stateless systems like Clear Linux
|
|
||||||
altOsRelease = "/usr/lib/os-release"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetOperatingSystem gets the name of the current operating system.
|
|
||||||
func GetOperatingSystem() (string, error) {
|
|
||||||
osReleaseFile, err := os.Open(etcOsRelease)
|
|
||||||
if err != nil {
|
|
||||||
if !os.IsNotExist(err) {
|
|
||||||
return "", fmt.Errorf("Error opening %s: %v", etcOsRelease, err)
|
|
||||||
}
|
|
||||||
osReleaseFile, err = os.Open(altOsRelease)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("Error opening %s: %v", altOsRelease, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defer osReleaseFile.Close()
|
|
||||||
|
|
||||||
var prettyName string
|
|
||||||
scanner := bufio.NewScanner(osReleaseFile)
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
if strings.HasPrefix(line, "PRETTY_NAME=") {
|
|
||||||
data := strings.SplitN(line, "=", 2)
|
|
||||||
prettyNames, err := shellwords.Parse(data[1])
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("PRETTY_NAME is invalid: %s", err.Error())
|
|
||||||
}
|
|
||||||
if len(prettyNames) != 1 {
|
|
||||||
return "", fmt.Errorf("PRETTY_NAME needs to be enclosed by quotes if they have spaces: %s", data[1])
|
|
||||||
}
|
|
||||||
prettyName = prettyNames[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if prettyName != "" {
|
|
||||||
return prettyName, nil
|
|
||||||
}
|
|
||||||
// If not set, defaults to PRETTY_NAME="Linux"
|
|
||||||
// c.f. http://www.freedesktop.org/software/systemd/man/os-release.html
|
|
||||||
return "Linux", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsContainerized returns true if we are running inside a container.
|
|
||||||
func IsContainerized() (bool, error) {
|
|
||||||
b, err := ioutil.ReadFile(proc1Cgroup)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
for _, line := range bytes.Split(b, []byte{'\n'}) {
|
|
||||||
if len(line) > 0 && !bytes.HasSuffix(line, []byte{'/'}) && !bytes.HasSuffix(line, []byte("init.scope")) {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
// +build solaris,cgo
|
|
||||||
|
|
||||||
package operatingsystem
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <zone.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"io/ioutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
var etcOsRelease = "/etc/release"
|
|
||||||
|
|
||||||
// GetOperatingSystem gets the name of the current operating system.
|
|
||||||
func GetOperatingSystem() (string, error) {
|
|
||||||
b, err := ioutil.ReadFile(etcOsRelease)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if i := bytes.Index(b, []byte("\n")); i >= 0 {
|
|
||||||
b = bytes.Trim(b[:i], " ")
|
|
||||||
return string(b), nil
|
|
||||||
}
|
|
||||||
return "", errors.New("release not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsContainerized returns true if we are running inside a container.
|
|
||||||
func IsContainerized() (bool, error) {
|
|
||||||
if C.getzoneid() != 0 {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
// +build freebsd darwin
|
|
||||||
|
|
||||||
package operatingsystem
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os/exec"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetOperatingSystem gets the name of the current operating system.
|
|
||||||
func GetOperatingSystem() (string, error) {
|
|
||||||
cmd := exec.Command("uname", "-s")
|
|
||||||
osName, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(osName), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsContainerized returns true if we are running inside a container.
|
|
||||||
// No-op on FreeBSD and Darwin, always returns false.
|
|
||||||
func IsContainerized() (bool, error) {
|
|
||||||
// TODO: Implement jail detection for freeBSD
|
|
||||||
return false, errors.New("Cannot detect if we are in container")
|
|
||||||
}
|
|
|
@ -1,247 +0,0 @@
|
||||||
// +build linux freebsd
|
|
||||||
|
|
||||||
package operatingsystem
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGetOperatingSystem(t *testing.T) {
|
|
||||||
var backup = etcOsRelease
|
|
||||||
|
|
||||||
invalids := []struct {
|
|
||||||
content string
|
|
||||||
errorExpected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
`PRETTY_NAME=Source Mage GNU/Linux
|
|
||||||
PRETTY_NAME=Ubuntu 14.04.LTS`,
|
|
||||||
"PRETTY_NAME needs to be enclosed by quotes if they have spaces: Source Mage GNU/Linux",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`PRETTY_NAME="Ubuntu Linux
|
|
||||||
PRETTY_NAME=Ubuntu 14.04.LTS`,
|
|
||||||
"PRETTY_NAME is invalid: invalid command line string",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`PRETTY_NAME=Ubuntu'
|
|
||||||
PRETTY_NAME=Ubuntu 14.04.LTS`,
|
|
||||||
"PRETTY_NAME is invalid: invalid command line string",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`PRETTY_NAME'
|
|
||||||
PRETTY_NAME=Ubuntu 14.04.LTS`,
|
|
||||||
"PRETTY_NAME needs to be enclosed by quotes if they have spaces: Ubuntu 14.04.LTS",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
valids := []struct {
|
|
||||||
content string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
`NAME="Ubuntu"
|
|
||||||
PRETTY_NAME_AGAIN="Ubuntu 14.04.LTS"
|
|
||||||
VERSION="14.04, Trusty Tahr"
|
|
||||||
ID=ubuntu
|
|
||||||
ID_LIKE=debian
|
|
||||||
VERSION_ID="14.04"
|
|
||||||
HOME_URL="http://www.ubuntu.com/"
|
|
||||||
SUPPORT_URL="http://help.ubuntu.com/"
|
|
||||||
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`,
|
|
||||||
"Linux",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`NAME="Ubuntu"
|
|
||||||
VERSION="14.04, Trusty Tahr"
|
|
||||||
ID=ubuntu
|
|
||||||
ID_LIKE=debian
|
|
||||||
VERSION_ID="14.04"
|
|
||||||
HOME_URL="http://www.ubuntu.com/"
|
|
||||||
SUPPORT_URL="http://help.ubuntu.com/"
|
|
||||||
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`,
|
|
||||||
"Linux",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`NAME=Gentoo
|
|
||||||
ID=gentoo
|
|
||||||
PRETTY_NAME="Gentoo/Linux"
|
|
||||||
ANSI_COLOR="1;32"
|
|
||||||
HOME_URL="http://www.gentoo.org/"
|
|
||||||
SUPPORT_URL="http://www.gentoo.org/main/en/support.xml"
|
|
||||||
BUG_REPORT_URL="https://bugs.gentoo.org/"
|
|
||||||
`,
|
|
||||||
"Gentoo/Linux",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`NAME="Ubuntu"
|
|
||||||
VERSION="14.04, Trusty Tahr"
|
|
||||||
ID=ubuntu
|
|
||||||
ID_LIKE=debian
|
|
||||||
PRETTY_NAME="Ubuntu 14.04 LTS"
|
|
||||||
VERSION_ID="14.04"
|
|
||||||
HOME_URL="http://www.ubuntu.com/"
|
|
||||||
SUPPORT_URL="http://help.ubuntu.com/"
|
|
||||||
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`,
|
|
||||||
"Ubuntu 14.04 LTS",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`NAME="Ubuntu"
|
|
||||||
VERSION="14.04, Trusty Tahr"
|
|
||||||
ID=ubuntu
|
|
||||||
ID_LIKE=debian
|
|
||||||
PRETTY_NAME='Ubuntu 14.04 LTS'`,
|
|
||||||
"Ubuntu 14.04 LTS",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`PRETTY_NAME=Source
|
|
||||||
NAME="Source Mage"`,
|
|
||||||
"Source",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`PRETTY_NAME=Source
|
|
||||||
PRETTY_NAME="Source Mage"`,
|
|
||||||
"Source Mage",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
dir := os.TempDir()
|
|
||||||
etcOsRelease = filepath.Join(dir, "etcOsRelease")
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
os.Remove(etcOsRelease)
|
|
||||||
etcOsRelease = backup
|
|
||||||
}()
|
|
||||||
|
|
||||||
for _, elt := range invalids {
|
|
||||||
if err := ioutil.WriteFile(etcOsRelease, []byte(elt.content), 0600); err != nil {
|
|
||||||
t.Fatalf("failed to write to %s: %v", etcOsRelease, err)
|
|
||||||
}
|
|
||||||
s, err := GetOperatingSystem()
|
|
||||||
if err == nil || err.Error() != elt.errorExpected {
|
|
||||||
t.Fatalf("Expected an error %q, got %q (err: %v)", elt.errorExpected, s, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, elt := range valids {
|
|
||||||
if err := ioutil.WriteFile(etcOsRelease, []byte(elt.content), 0600); err != nil {
|
|
||||||
t.Fatalf("failed to write to %s: %v", etcOsRelease, err)
|
|
||||||
}
|
|
||||||
s, err := GetOperatingSystem()
|
|
||||||
if err != nil || s != elt.expected {
|
|
||||||
t.Fatalf("Expected %q, got %q (err: %v)", elt.expected, s, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsContainerized(t *testing.T) {
|
|
||||||
var (
|
|
||||||
backup = proc1Cgroup
|
|
||||||
nonContainerizedProc1Cgroupsystemd226 = []byte(`9:memory:/init.scope
|
|
||||||
8:net_cls,net_prio:/
|
|
||||||
7:cpuset:/
|
|
||||||
6:freezer:/
|
|
||||||
5:devices:/init.scope
|
|
||||||
4:blkio:/init.scope
|
|
||||||
3:cpu,cpuacct:/init.scope
|
|
||||||
2:perf_event:/
|
|
||||||
1:name=systemd:/init.scope
|
|
||||||
`)
|
|
||||||
nonContainerizedProc1Cgroup = []byte(`14:name=systemd:/
|
|
||||||
13:hugetlb:/
|
|
||||||
12:net_prio:/
|
|
||||||
11:perf_event:/
|
|
||||||
10:bfqio:/
|
|
||||||
9:blkio:/
|
|
||||||
8:net_cls:/
|
|
||||||
7:freezer:/
|
|
||||||
6:devices:/
|
|
||||||
5:memory:/
|
|
||||||
4:cpuacct:/
|
|
||||||
3:cpu:/
|
|
||||||
2:cpuset:/
|
|
||||||
`)
|
|
||||||
containerizedProc1Cgroup = []byte(`9:perf_event:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
|
||||||
8:blkio:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
|
||||||
7:net_cls:/
|
|
||||||
6:freezer:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
|
||||||
5:devices:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
|
||||||
4:memory:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
|
||||||
3:cpuacct:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
|
||||||
2:cpu:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
|
||||||
1:cpuset:/`)
|
|
||||||
)
|
|
||||||
|
|
||||||
dir := os.TempDir()
|
|
||||||
proc1Cgroup = filepath.Join(dir, "proc1Cgroup")
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
os.Remove(proc1Cgroup)
|
|
||||||
proc1Cgroup = backup
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := ioutil.WriteFile(proc1Cgroup, nonContainerizedProc1Cgroup, 0600); err != nil {
|
|
||||||
t.Fatalf("failed to write to %s: %v", proc1Cgroup, err)
|
|
||||||
}
|
|
||||||
inContainer, err := IsContainerized()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if inContainer {
|
|
||||||
t.Fatal("Wrongly assuming containerized")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ioutil.WriteFile(proc1Cgroup, nonContainerizedProc1Cgroupsystemd226, 0600); err != nil {
|
|
||||||
t.Fatalf("failed to write to %s: %v", proc1Cgroup, err)
|
|
||||||
}
|
|
||||||
inContainer, err = IsContainerized()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if inContainer {
|
|
||||||
t.Fatal("Wrongly assuming containerized for systemd /init.scope cgroup layout")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ioutil.WriteFile(proc1Cgroup, containerizedProc1Cgroup, 0600); err != nil {
|
|
||||||
t.Fatalf("failed to write to %s: %v", proc1Cgroup, err)
|
|
||||||
}
|
|
||||||
inContainer, err = IsContainerized()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !inContainer {
|
|
||||||
t.Fatal("Wrongly assuming non-containerized")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOsReleaseFallback(t *testing.T) {
|
|
||||||
var backup = etcOsRelease
|
|
||||||
var altBackup = altOsRelease
|
|
||||||
dir := os.TempDir()
|
|
||||||
etcOsRelease = filepath.Join(dir, "etcOsRelease")
|
|
||||||
altOsRelease = filepath.Join(dir, "altOsRelease")
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
os.Remove(dir)
|
|
||||||
etcOsRelease = backup
|
|
||||||
altOsRelease = altBackup
|
|
||||||
}()
|
|
||||||
content := `NAME=Gentoo
|
|
||||||
ID=gentoo
|
|
||||||
PRETTY_NAME="Gentoo/Linux"
|
|
||||||
ANSI_COLOR="1;32"
|
|
||||||
HOME_URL="http://www.gentoo.org/"
|
|
||||||
SUPPORT_URL="http://www.gentoo.org/main/en/support.xml"
|
|
||||||
BUG_REPORT_URL="https://bugs.gentoo.org/"
|
|
||||||
`
|
|
||||||
if err := ioutil.WriteFile(altOsRelease, []byte(content), 0600); err != nil {
|
|
||||||
t.Fatalf("failed to write to %s: %v", etcOsRelease, err)
|
|
||||||
}
|
|
||||||
s, err := GetOperatingSystem()
|
|
||||||
if err != nil || s != "Gentoo/Linux" {
|
|
||||||
t.Fatalf("Expected %q, got %q (err: %v)", "Gentoo/Linux", s, err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
package operatingsystem
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// See https://code.google.com/p/go/source/browse/src/pkg/mime/type_windows.go?r=d14520ac25bf6940785aabb71f5be453a286f58c
|
|
||||||
// for a similar sample
|
|
||||||
|
|
||||||
// GetOperatingSystem gets the name of the current operating system.
|
|
||||||
func GetOperatingSystem() (string, error) {
|
|
||||||
|
|
||||||
var h syscall.Handle
|
|
||||||
|
|
||||||
// Default return value
|
|
||||||
ret := "Unknown Operating System"
|
|
||||||
|
|
||||||
if err := syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE,
|
|
||||||
syscall.StringToUTF16Ptr(`SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\`),
|
|
||||||
0,
|
|
||||||
syscall.KEY_READ,
|
|
||||||
&h); err != nil {
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
defer syscall.RegCloseKey(h)
|
|
||||||
|
|
||||||
var buf [1 << 10]uint16
|
|
||||||
var typ uint32
|
|
||||||
n := uint32(len(buf) * 2) // api expects array of bytes, not uint16
|
|
||||||
|
|
||||||
if err := syscall.RegQueryValueEx(h,
|
|
||||||
syscall.StringToUTF16Ptr("ProductName"),
|
|
||||||
nil,
|
|
||||||
&typ,
|
|
||||||
(*byte)(unsafe.Pointer(&buf[0])),
|
|
||||||
&n); err != nil {
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
ret = syscall.UTF16ToString(buf[:])
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsContainerized returns true if we are running inside a container.
|
|
||||||
// No-op on Windows, always returns false.
|
|
||||||
func IsContainerized() (bool, error) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
// Package pidfile provides structure and helper functions to create and remove
|
|
||||||
// PID file. A PID file is usually a file used to store the process ID of a
|
|
||||||
// running process.
|
|
||||||
package pidfile
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PIDFile is a file used to store the process ID of a running process.
|
|
||||||
type PIDFile struct {
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkPIDFileAlreadyExists(path string) error {
|
|
||||||
if pidByte, err := ioutil.ReadFile(path); err == nil {
|
|
||||||
pidString := strings.TrimSpace(string(pidByte))
|
|
||||||
if pid, err := strconv.Atoi(pidString); err == nil {
|
|
||||||
if processExists(pid) {
|
|
||||||
return fmt.Errorf("pid file found, ensure docker is not running or delete %s", path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a PIDfile using the specified path.
|
|
||||||
func New(path string) (*PIDFile, error) {
|
|
||||||
if err := checkPIDFileAlreadyExists(path); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := ioutil.WriteFile(path, []byte(fmt.Sprintf("%d", os.Getpid())), 0644); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &PIDFile{path: path}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove removes the PIDFile.
|
|
||||||
func (file PIDFile) Remove() error {
|
|
||||||
if err := os.Remove(file.path); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
package pidfile
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewAndRemove(t *testing.T) {
|
|
||||||
dir, err := ioutil.TempDir(os.TempDir(), "test-pidfile")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Could not create test directory")
|
|
||||||
}
|
|
||||||
|
|
||||||
path := filepath.Join(dir, "testfile")
|
|
||||||
file, err := New(path)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Could not create test file", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = New(path)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Test file creation not blocked")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := file.Remove(); err != nil {
|
|
||||||
t.Fatal("Could not delete created test file")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRemoveInvalidPath(t *testing.T) {
|
|
||||||
file := PIDFile{path: filepath.Join("foo", "bar")}
|
|
||||||
|
|
||||||
if err := file.Remove(); err == nil {
|
|
||||||
t.Fatal("Non-existing file doesn't give an error on delete")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package pidfile
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
func processExists(pid int) bool {
|
|
||||||
if _, err := os.Stat(filepath.Join("/proc", strconv.Itoa(pid))); err == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
package pidfile
|
|
||||||
|
|
||||||
import "syscall"
|
|
||||||
|
|
||||||
const (
|
|
||||||
processQueryLimitedInformation = 0x1000
|
|
||||||
|
|
||||||
stillActive = 259
|
|
||||||
)
|
|
||||||
|
|
||||||
func processExists(pid int) bool {
|
|
||||||
h, err := syscall.OpenProcess(processQueryLimitedInformation, false, uint32(pid))
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
var c uint32
|
|
||||||
err = syscall.GetExitCodeProcess(h, &c)
|
|
||||||
syscall.Close(h)
|
|
||||||
if err != nil {
|
|
||||||
return c == stillActive
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
// +build !linux,!windows
|
|
||||||
|
|
||||||
package sysinfo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NumCPU returns the number of CPUs
|
|
||||||
func NumCPU() int {
|
|
||||||
return runtime.NumCPU()
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package sysinfo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// numCPU queries the system for the count of threads available
|
|
||||||
// for use to this process.
|
|
||||||
//
|
|
||||||
// Issues two syscalls.
|
|
||||||
// Returns 0 on errors. Use |runtime.NumCPU| in that case.
|
|
||||||
func numCPU() int {
|
|
||||||
// Gets the affinity mask for a process: The very one invoking this function.
|
|
||||||
pid, _, _ := syscall.RawSyscall(syscall.SYS_GETPID, 0, 0, 0)
|
|
||||||
|
|
||||||
var mask [1024 / 64]uintptr
|
|
||||||
_, _, err := syscall.RawSyscall(syscall.SYS_SCHED_GETAFFINITY, pid, uintptr(len(mask)*8), uintptr(unsafe.Pointer(&mask[0])))
|
|
||||||
if err != 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// For every available thread a bit is set in the mask.
|
|
||||||
ncpu := 0
|
|
||||||
for _, e := range mask {
|
|
||||||
if e == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ncpu += int(popcnt(uint64(e)))
|
|
||||||
}
|
|
||||||
return ncpu
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumCPU returns the number of CPUs which are currently online
|
|
||||||
func NumCPU() int {
|
|
||||||
if ncpu := numCPU(); ncpu > 0 {
|
|
||||||
return ncpu
|
|
||||||
}
|
|
||||||
return runtime.NumCPU()
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package sysinfo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
|
||||||
getCurrentProcess = kernel32.NewProc("GetCurrentProcess")
|
|
||||||
getProcessAffinityMask = kernel32.NewProc("GetProcessAffinityMask")
|
|
||||||
)
|
|
||||||
|
|
||||||
func numCPU() int {
|
|
||||||
// Gets the affinity mask for a process
|
|
||||||
var mask, sysmask uintptr
|
|
||||||
currentProcess, _, _ := getCurrentProcess.Call()
|
|
||||||
ret, _, _ := getProcessAffinityMask.Call(currentProcess, uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
|
|
||||||
if ret == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
// For every available thread a bit is set in the mask.
|
|
||||||
ncpu := int(popcnt(uint64(mask)))
|
|
||||||
return ncpu
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumCPU returns the number of CPUs which are currently online
|
|
||||||
func NumCPU() int {
|
|
||||||
if ncpu := numCPU(); ncpu > 0 {
|
|
||||||
return ncpu
|
|
||||||
}
|
|
||||||
return runtime.NumCPU()
|
|
||||||
}
|
|
|
@ -1,138 +0,0 @@
|
||||||
package sysinfo
|
|
||||||
|
|
||||||
import "github.com/containers/storage/pkg/parsers"
|
|
||||||
|
|
||||||
// SysInfo stores information about which features a kernel supports.
|
|
||||||
// TODO Windows: Factor out platform specific capabilities.
|
|
||||||
type SysInfo struct {
|
|
||||||
// Whether the kernel supports AppArmor or not
|
|
||||||
AppArmor bool
|
|
||||||
// Whether the kernel supports Seccomp or not
|
|
||||||
Seccomp bool
|
|
||||||
|
|
||||||
cgroupMemInfo
|
|
||||||
cgroupCPUInfo
|
|
||||||
cgroupBlkioInfo
|
|
||||||
cgroupCpusetInfo
|
|
||||||
cgroupPids
|
|
||||||
|
|
||||||
// Whether IPv4 forwarding is supported or not, if this was disabled, networking will not work
|
|
||||||
IPv4ForwardingDisabled bool
|
|
||||||
|
|
||||||
// Whether bridge-nf-call-iptables is supported or not
|
|
||||||
BridgeNFCallIPTablesDisabled bool
|
|
||||||
|
|
||||||
// Whether bridge-nf-call-ip6tables is supported or not
|
|
||||||
BridgeNFCallIP6TablesDisabled bool
|
|
||||||
|
|
||||||
// Whether the cgroup has the mountpoint of "devices" or not
|
|
||||||
CgroupDevicesEnabled bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type cgroupMemInfo struct {
|
|
||||||
// Whether memory limit is supported or not
|
|
||||||
MemoryLimit bool
|
|
||||||
|
|
||||||
// Whether swap limit is supported or not
|
|
||||||
SwapLimit bool
|
|
||||||
|
|
||||||
// Whether soft limit is supported or not
|
|
||||||
MemoryReservation bool
|
|
||||||
|
|
||||||
// Whether OOM killer disable is supported or not
|
|
||||||
OomKillDisable bool
|
|
||||||
|
|
||||||
// Whether memory swappiness is supported or not
|
|
||||||
MemorySwappiness bool
|
|
||||||
|
|
||||||
// Whether kernel memory limit is supported or not
|
|
||||||
KernelMemory bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type cgroupCPUInfo struct {
|
|
||||||
// Whether CPU shares is supported or not
|
|
||||||
CPUShares bool
|
|
||||||
|
|
||||||
// Whether CPU CFS(Completely Fair Scheduler) period is supported or not
|
|
||||||
CPUCfsPeriod bool
|
|
||||||
|
|
||||||
// Whether CPU CFS(Completely Fair Scheduler) quota is supported or not
|
|
||||||
CPUCfsQuota bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type cgroupBlkioInfo struct {
|
|
||||||
// Whether Block IO weight is supported or not
|
|
||||||
BlkioWeight bool
|
|
||||||
|
|
||||||
// Whether Block IO weight_device is supported or not
|
|
||||||
BlkioWeightDevice bool
|
|
||||||
|
|
||||||
// Whether Block IO read limit in bytes per second is supported or not
|
|
||||||
BlkioReadBpsDevice bool
|
|
||||||
|
|
||||||
// Whether Block IO write limit in bytes per second is supported or not
|
|
||||||
BlkioWriteBpsDevice bool
|
|
||||||
|
|
||||||
// Whether Block IO read limit in IO per second is supported or not
|
|
||||||
BlkioReadIOpsDevice bool
|
|
||||||
|
|
||||||
// Whether Block IO write limit in IO per second is supported or not
|
|
||||||
BlkioWriteIOpsDevice bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type cgroupCpusetInfo struct {
|
|
||||||
// Whether Cpuset is supported or not
|
|
||||||
Cpuset bool
|
|
||||||
|
|
||||||
// Available Cpuset's cpus
|
|
||||||
Cpus string
|
|
||||||
|
|
||||||
// Available Cpuset's memory nodes
|
|
||||||
Mems string
|
|
||||||
}
|
|
||||||
|
|
||||||
type cgroupPids struct {
|
|
||||||
// Whether Pids Limit is supported or not
|
|
||||||
PidsLimit bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsCpusetCpusAvailable returns `true` if the provided string set is contained
|
|
||||||
// in cgroup's cpuset.cpus set, `false` otherwise.
|
|
||||||
// If error is not nil a parsing error occurred.
|
|
||||||
func (c cgroupCpusetInfo) IsCpusetCpusAvailable(provided string) (bool, error) {
|
|
||||||
return isCpusetListAvailable(provided, c.Cpus)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsCpusetMemsAvailable returns `true` if the provided string set is contained
|
|
||||||
// in cgroup's cpuset.mems set, `false` otherwise.
|
|
||||||
// If error is not nil a parsing error occurred.
|
|
||||||
func (c cgroupCpusetInfo) IsCpusetMemsAvailable(provided string) (bool, error) {
|
|
||||||
return isCpusetListAvailable(provided, c.Mems)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isCpusetListAvailable(provided, available string) (bool, error) {
|
|
||||||
parsedProvided, err := parsers.ParseUintList(provided)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
parsedAvailable, err := parsers.ParseUintList(available)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
for k := range parsedProvided {
|
|
||||||
if !parsedAvailable[k] {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns bit count of 1, used by NumCPU
|
|
||||||
func popcnt(x uint64) (n byte) {
|
|
||||||
x -= (x >> 1) & 0x5555555555555555
|
|
||||||
x = (x>>2)&0x3333333333333333 + x&0x3333333333333333
|
|
||||||
x += x >> 4
|
|
||||||
x &= 0x0f0f0f0f0f0f0f0f
|
|
||||||
x *= 0x0101010101010101
|
|
||||||
return byte(x >> 56)
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
package sysinfo
|
|
||||||
|
|
||||||
// New returns an empty SysInfo for freebsd for now.
|
|
||||||
func New(quiet bool) *SysInfo {
|
|
||||||
sysInfo := &SysInfo{}
|
|
||||||
return sysInfo
|
|
||||||
}
|
|
|
@ -1,246 +0,0 @@
|
||||||
package sysinfo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// SeccompModeFilter refers to the syscall argument SECCOMP_MODE_FILTER.
|
|
||||||
SeccompModeFilter = uintptr(2)
|
|
||||||
)
|
|
||||||
|
|
||||||
func findCgroupMountpoints() (map[string]string, error) {
|
|
||||||
cgMounts, err := cgroups.GetCgroupMounts()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to parse cgroup information: %v", err)
|
|
||||||
}
|
|
||||||
mps := make(map[string]string)
|
|
||||||
for _, m := range cgMounts {
|
|
||||||
for _, ss := range m.Subsystems {
|
|
||||||
mps[ss] = m.Mountpoint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mps, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns a new SysInfo, using the filesystem to detect which features
|
|
||||||
// the kernel supports. If `quiet` is `false` warnings are printed in logs
|
|
||||||
// whenever an error occurs or misconfigurations are present.
|
|
||||||
func New(quiet bool) *SysInfo {
|
|
||||||
sysInfo := &SysInfo{}
|
|
||||||
cgMounts, err := findCgroupMountpoints()
|
|
||||||
if err != nil {
|
|
||||||
logrus.Warnf("Failed to parse cgroup information: %v", err)
|
|
||||||
} else {
|
|
||||||
sysInfo.cgroupMemInfo = checkCgroupMem(cgMounts, quiet)
|
|
||||||
sysInfo.cgroupCPUInfo = checkCgroupCPU(cgMounts, quiet)
|
|
||||||
sysInfo.cgroupBlkioInfo = checkCgroupBlkioInfo(cgMounts, quiet)
|
|
||||||
sysInfo.cgroupCpusetInfo = checkCgroupCpusetInfo(cgMounts, quiet)
|
|
||||||
sysInfo.cgroupPids = checkCgroupPids(quiet)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, ok := cgMounts["devices"]
|
|
||||||
sysInfo.CgroupDevicesEnabled = ok
|
|
||||||
|
|
||||||
sysInfo.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward")
|
|
||||||
sysInfo.BridgeNFCallIPTablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables")
|
|
||||||
sysInfo.BridgeNFCallIP6TablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables")
|
|
||||||
|
|
||||||
// Check if AppArmor is supported.
|
|
||||||
if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) {
|
|
||||||
sysInfo.AppArmor = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if Seccomp is supported, via CONFIG_SECCOMP.
|
|
||||||
if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_GET_SECCOMP, 0, 0); err != syscall.EINVAL {
|
|
||||||
// Make sure the kernel has CONFIG_SECCOMP_FILTER.
|
|
||||||
if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_SECCOMP, SeccompModeFilter, 0); err != syscall.EINVAL {
|
|
||||||
sysInfo.Seccomp = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sysInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkCgroupMem reads the memory information from the memory cgroup mount point.
|
|
||||||
func checkCgroupMem(cgMounts map[string]string, quiet bool) cgroupMemInfo {
|
|
||||||
mountPoint, ok := cgMounts["memory"]
|
|
||||||
if !ok {
|
|
||||||
if !quiet {
|
|
||||||
logrus.Warn("Your kernel does not support cgroup memory limit")
|
|
||||||
}
|
|
||||||
return cgroupMemInfo{}
|
|
||||||
}
|
|
||||||
|
|
||||||
swapLimit := cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes")
|
|
||||||
if !quiet && !swapLimit {
|
|
||||||
logrus.Warn("Your kernel does not support swap memory limit.")
|
|
||||||
}
|
|
||||||
memoryReservation := cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes")
|
|
||||||
if !quiet && !memoryReservation {
|
|
||||||
logrus.Warn("Your kernel does not support memory reservation.")
|
|
||||||
}
|
|
||||||
oomKillDisable := cgroupEnabled(mountPoint, "memory.oom_control")
|
|
||||||
if !quiet && !oomKillDisable {
|
|
||||||
logrus.Warn("Your kernel does not support oom control.")
|
|
||||||
}
|
|
||||||
memorySwappiness := cgroupEnabled(mountPoint, "memory.swappiness")
|
|
||||||
if !quiet && !memorySwappiness {
|
|
||||||
logrus.Warn("Your kernel does not support memory swappiness.")
|
|
||||||
}
|
|
||||||
kernelMemory := cgroupEnabled(mountPoint, "memory.kmem.limit_in_bytes")
|
|
||||||
if !quiet && !kernelMemory {
|
|
||||||
logrus.Warn("Your kernel does not support kernel memory limit.")
|
|
||||||
}
|
|
||||||
|
|
||||||
return cgroupMemInfo{
|
|
||||||
MemoryLimit: true,
|
|
||||||
SwapLimit: swapLimit,
|
|
||||||
MemoryReservation: memoryReservation,
|
|
||||||
OomKillDisable: oomKillDisable,
|
|
||||||
MemorySwappiness: memorySwappiness,
|
|
||||||
KernelMemory: kernelMemory,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkCgroupCPU reads the cpu information from the cpu cgroup mount point.
|
|
||||||
func checkCgroupCPU(cgMounts map[string]string, quiet bool) cgroupCPUInfo {
|
|
||||||
mountPoint, ok := cgMounts["cpu"]
|
|
||||||
if !ok {
|
|
||||||
if !quiet {
|
|
||||||
logrus.Warn("Unable to find cpu cgroup in mounts")
|
|
||||||
}
|
|
||||||
return cgroupCPUInfo{}
|
|
||||||
}
|
|
||||||
|
|
||||||
cpuShares := cgroupEnabled(mountPoint, "cpu.shares")
|
|
||||||
if !quiet && !cpuShares {
|
|
||||||
logrus.Warn("Your kernel does not support cgroup cpu shares")
|
|
||||||
}
|
|
||||||
|
|
||||||
cpuCfsPeriod := cgroupEnabled(mountPoint, "cpu.cfs_period_us")
|
|
||||||
if !quiet && !cpuCfsPeriod {
|
|
||||||
logrus.Warn("Your kernel does not support cgroup cfs period")
|
|
||||||
}
|
|
||||||
|
|
||||||
cpuCfsQuota := cgroupEnabled(mountPoint, "cpu.cfs_quota_us")
|
|
||||||
if !quiet && !cpuCfsQuota {
|
|
||||||
logrus.Warn("Your kernel does not support cgroup cfs quotas")
|
|
||||||
}
|
|
||||||
return cgroupCPUInfo{
|
|
||||||
CPUShares: cpuShares,
|
|
||||||
CPUCfsPeriod: cpuCfsPeriod,
|
|
||||||
CPUCfsQuota: cpuCfsQuota,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkCgroupBlkioInfo reads the blkio information from the blkio cgroup mount point.
|
|
||||||
func checkCgroupBlkioInfo(cgMounts map[string]string, quiet bool) cgroupBlkioInfo {
|
|
||||||
mountPoint, ok := cgMounts["blkio"]
|
|
||||||
if !ok {
|
|
||||||
if !quiet {
|
|
||||||
logrus.Warn("Unable to find blkio cgroup in mounts")
|
|
||||||
}
|
|
||||||
return cgroupBlkioInfo{}
|
|
||||||
}
|
|
||||||
|
|
||||||
weight := cgroupEnabled(mountPoint, "blkio.weight")
|
|
||||||
if !quiet && !weight {
|
|
||||||
logrus.Warn("Your kernel does not support cgroup blkio weight")
|
|
||||||
}
|
|
||||||
|
|
||||||
weightDevice := cgroupEnabled(mountPoint, "blkio.weight_device")
|
|
||||||
if !quiet && !weightDevice {
|
|
||||||
logrus.Warn("Your kernel does not support cgroup blkio weight_device")
|
|
||||||
}
|
|
||||||
|
|
||||||
readBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device")
|
|
||||||
if !quiet && !readBpsDevice {
|
|
||||||
logrus.Warn("Your kernel does not support cgroup blkio throttle.read_bps_device")
|
|
||||||
}
|
|
||||||
|
|
||||||
writeBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device")
|
|
||||||
if !quiet && !writeBpsDevice {
|
|
||||||
logrus.Warn("Your kernel does not support cgroup blkio throttle.write_bps_device")
|
|
||||||
}
|
|
||||||
readIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_iops_device")
|
|
||||||
if !quiet && !readIOpsDevice {
|
|
||||||
logrus.Warn("Your kernel does not support cgroup blkio throttle.read_iops_device")
|
|
||||||
}
|
|
||||||
|
|
||||||
writeIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_iops_device")
|
|
||||||
if !quiet && !writeIOpsDevice {
|
|
||||||
logrus.Warn("Your kernel does not support cgroup blkio throttle.write_iops_device")
|
|
||||||
}
|
|
||||||
return cgroupBlkioInfo{
|
|
||||||
BlkioWeight: weight,
|
|
||||||
BlkioWeightDevice: weightDevice,
|
|
||||||
BlkioReadBpsDevice: readBpsDevice,
|
|
||||||
BlkioWriteBpsDevice: writeBpsDevice,
|
|
||||||
BlkioReadIOpsDevice: readIOpsDevice,
|
|
||||||
BlkioWriteIOpsDevice: writeIOpsDevice,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkCgroupCpusetInfo reads the cpuset information from the cpuset cgroup mount point.
|
|
||||||
func checkCgroupCpusetInfo(cgMounts map[string]string, quiet bool) cgroupCpusetInfo {
|
|
||||||
mountPoint, ok := cgMounts["cpuset"]
|
|
||||||
if !ok {
|
|
||||||
if !quiet {
|
|
||||||
logrus.Warn("Unable to find cpuset cgroup in mounts")
|
|
||||||
}
|
|
||||||
return cgroupCpusetInfo{}
|
|
||||||
}
|
|
||||||
|
|
||||||
cpus, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.cpus"))
|
|
||||||
if err != nil {
|
|
||||||
return cgroupCpusetInfo{}
|
|
||||||
}
|
|
||||||
|
|
||||||
mems, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.mems"))
|
|
||||||
if err != nil {
|
|
||||||
return cgroupCpusetInfo{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cgroupCpusetInfo{
|
|
||||||
Cpuset: true,
|
|
||||||
Cpus: strings.TrimSpace(string(cpus)),
|
|
||||||
Mems: strings.TrimSpace(string(mems)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkCgroupPids reads the pids information from the pids cgroup mount point.
|
|
||||||
func checkCgroupPids(quiet bool) cgroupPids {
|
|
||||||
_, err := cgroups.FindCgroupMountpoint("pids")
|
|
||||||
if err != nil {
|
|
||||||
if !quiet {
|
|
||||||
logrus.Warn(err)
|
|
||||||
}
|
|
||||||
return cgroupPids{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cgroupPids{
|
|
||||||
PidsLimit: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cgroupEnabled(mountPoint, name string) bool {
|
|
||||||
_, err := os.Stat(path.Join(mountPoint, name))
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readProcBool(path string) bool {
|
|
||||||
val, err := ioutil.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(string(val)) == "1"
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
package sysinfo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestReadProcBool(t *testing.T) {
|
|
||||||
tmpDir, err := ioutil.TempDir("", "test-sysinfo-proc")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmpDir)
|
|
||||||
|
|
||||||
procFile := filepath.Join(tmpDir, "read-proc-bool")
|
|
||||||
if err := ioutil.WriteFile(procFile, []byte("1"), 644); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !readProcBool(procFile) {
|
|
||||||
t.Fatal("expected proc bool to be true, got false")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ioutil.WriteFile(procFile, []byte("0"), 644); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if readProcBool(procFile) {
|
|
||||||
t.Fatal("expected proc bool to be false, got false")
|
|
||||||
}
|
|
||||||
|
|
||||||
if readProcBool(path.Join(tmpDir, "no-exist")) {
|
|
||||||
t.Fatal("should be false for non-existent entry")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCgroupEnabled(t *testing.T) {
|
|
||||||
cgroupDir, err := ioutil.TempDir("", "cgroup-test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(cgroupDir)
|
|
||||||
|
|
||||||
if cgroupEnabled(cgroupDir, "test") {
|
|
||||||
t.Fatal("cgroupEnabled should be false")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ioutil.WriteFile(path.Join(cgroupDir, "test"), []byte{}, 644); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !cgroupEnabled(cgroupDir, "test") {
|
|
||||||
t.Fatal("cgroupEnabled should be true")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,119 +0,0 @@
|
||||||
// +build solaris,cgo
|
|
||||||
|
|
||||||
package sysinfo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"os/exec"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo LDFLAGS: -llgrp
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/lgrp_user.h>
|
|
||||||
int getLgrpCount() {
|
|
||||||
lgrp_cookie_t lgrpcookie = LGRP_COOKIE_NONE;
|
|
||||||
uint_t nlgrps;
|
|
||||||
|
|
||||||
if ((lgrpcookie = lgrp_init(LGRP_VIEW_OS)) == LGRP_COOKIE_NONE) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
nlgrps = lgrp_nlgrps(lgrpcookie);
|
|
||||||
return nlgrps;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
// IsCPUSharesAvailable returns whether CPUShares setting is supported.
|
|
||||||
// We need FSS to be set as default scheduling class to support CPU Shares
|
|
||||||
func IsCPUSharesAvailable() bool {
|
|
||||||
cmd := exec.Command("/usr/sbin/dispadmin", "-d")
|
|
||||||
outBuf := new(bytes.Buffer)
|
|
||||||
errBuf := new(bytes.Buffer)
|
|
||||||
cmd.Stderr = errBuf
|
|
||||||
cmd.Stdout = outBuf
|
|
||||||
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return (strings.Contains(outBuf.String(), "FSS"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns a new SysInfo, using the filesystem to detect which features
|
|
||||||
// the kernel supports.
|
|
||||||
//NOTE Solaris: If we change the below capabilities be sure
|
|
||||||
// to update verifyPlatformContainerSettings() in daemon_solaris.go
|
|
||||||
func New(quiet bool) *SysInfo {
|
|
||||||
sysInfo := &SysInfo{}
|
|
||||||
sysInfo.cgroupMemInfo = setCgroupMem(quiet)
|
|
||||||
sysInfo.cgroupCPUInfo = setCgroupCPU(quiet)
|
|
||||||
sysInfo.cgroupBlkioInfo = setCgroupBlkioInfo(quiet)
|
|
||||||
sysInfo.cgroupCpusetInfo = setCgroupCPUsetInfo(quiet)
|
|
||||||
|
|
||||||
sysInfo.IPv4ForwardingDisabled = false
|
|
||||||
|
|
||||||
sysInfo.AppArmor = false
|
|
||||||
|
|
||||||
return sysInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
// setCgroupMem reads the memory information for Solaris.
|
|
||||||
func setCgroupMem(quiet bool) cgroupMemInfo {
|
|
||||||
|
|
||||||
return cgroupMemInfo{
|
|
||||||
MemoryLimit: true,
|
|
||||||
SwapLimit: true,
|
|
||||||
MemoryReservation: false,
|
|
||||||
OomKillDisable: false,
|
|
||||||
MemorySwappiness: false,
|
|
||||||
KernelMemory: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// setCgroupCPU reads the cpu information for Solaris.
|
|
||||||
func setCgroupCPU(quiet bool) cgroupCPUInfo {
|
|
||||||
|
|
||||||
return cgroupCPUInfo{
|
|
||||||
CPUShares: true,
|
|
||||||
CPUCfsPeriod: false,
|
|
||||||
CPUCfsQuota: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// blkio switches are not supported in Solaris.
|
|
||||||
func setCgroupBlkioInfo(quiet bool) cgroupBlkioInfo {
|
|
||||||
|
|
||||||
return cgroupBlkioInfo{
|
|
||||||
BlkioWeight: false,
|
|
||||||
BlkioWeightDevice: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// setCgroupCPUsetInfo reads the cpuset information for Solaris.
|
|
||||||
func setCgroupCPUsetInfo(quiet bool) cgroupCpusetInfo {
|
|
||||||
|
|
||||||
return cgroupCpusetInfo{
|
|
||||||
Cpuset: true,
|
|
||||||
Cpus: getCPUCount(),
|
|
||||||
Mems: getLgrpCount(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCPUCount() string {
|
|
||||||
ncpus := C.sysconf(C._SC_NPROCESSORS_ONLN)
|
|
||||||
if ncpus <= 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return strconv.FormatInt(int64(ncpus), 16)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLgrpCount() string {
|
|
||||||
nlgrps := C.getLgrpCount()
|
|
||||||
if nlgrps <= 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return strconv.FormatInt(int64(nlgrps), 16)
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
package sysinfo
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestIsCpusetListAvailable(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
provided string
|
|
||||||
available string
|
|
||||||
res bool
|
|
||||||
err bool
|
|
||||||
}{
|
|
||||||
{"1", "0-4", true, false},
|
|
||||||
{"01,3", "0-4", true, false},
|
|
||||||
{"", "0-7", true, false},
|
|
||||||
{"1--42", "0-7", false, true},
|
|
||||||
{"1-42", "00-1,8,,9", false, true},
|
|
||||||
{"1,41-42", "43,45", false, false},
|
|
||||||
{"0-3", "", false, false},
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
r, err := isCpusetListAvailable(c.provided, c.available)
|
|
||||||
if (c.err && err == nil) && r != c.res {
|
|
||||||
t.Fatalf("Expected pair: %v, %v for %s, %s. Got %v, %v instead", c.res, c.err, c.provided, c.available, (c.err && err == nil), r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
package sysinfo
|
|
||||||
|
|
||||||
// New returns an empty SysInfo for windows for now.
|
|
||||||
func New(quiet bool) *SysInfo {
|
|
||||||
sysInfo := &SysInfo{}
|
|
||||||
return sysInfo
|
|
||||||
}
|
|
Loading…
Reference in a new issue