Merge branch 'master' into docker-detect
commit
4fa2e414fb
6
Makefile
6
Makefile
|
|
@ -747,9 +747,9 @@ site: site/themes/docsy/assets/vendor/bootstrap/package.js out/hugo/hugo ## Serv
|
|||
out/mkcmp:
|
||||
GOOS=$(GOOS) GOARCH=$(GOARCH) go build -o $@ cmd/performance/mkcmp/main.go
|
||||
|
||||
.PHONY: out/performance-monitor
|
||||
out/performance-monitor:
|
||||
GOOS=$(GOOS) GOARCH=$(GOARCH) go build -o $@ cmd/performance/monitor/monitor.go
|
||||
.PHONY: out/performance-bot
|
||||
out/performance-bot:
|
||||
GOOS=$(GOOS) GOARCH=$(GOARCH) go build -o $@ cmd/performance/pr-bot/bot.go
|
||||
|
||||
.PHONY: compare
|
||||
compare: out/mkcmp out/minikube
|
||||
|
|
|
|||
|
|
@ -91,7 +91,6 @@ func init() {
|
|||
if err := viper.BindPFlags(deleteCmd.Flags()); err != nil {
|
||||
exit.Error(reason.InternalBindFlags, "unable to bind flags", err)
|
||||
}
|
||||
RootCmd.AddCommand(deleteCmd)
|
||||
}
|
||||
|
||||
// shotgun cleanup to delete orphaned docker container data
|
||||
|
|
|
|||
|
|
@ -829,7 +829,7 @@ func memoryLimits(drvName string) (int, int, error) {
|
|||
if err != nil {
|
||||
return -1, -1, err
|
||||
}
|
||||
containerLimit = int(s.TotalMemory / 1024 / 1024)
|
||||
containerLimit = util.ConvertBytesToMB(s.TotalMemory)
|
||||
}
|
||||
|
||||
return sysLimit, containerLimit, nil
|
||||
|
|
|
|||
|
|
@ -60,8 +60,6 @@ func init() {
|
|||
if err := viper.GetViper().BindPFlags(stopCmd.Flags()); err != nil {
|
||||
exit.Error(reason.InternalFlagsBind, "unable to bind flags", err)
|
||||
}
|
||||
|
||||
RootCmd.AddCommand(stopCmd)
|
||||
}
|
||||
|
||||
// runStop handles the executes the flow of "minikube stop"
|
||||
|
|
|
|||
|
|
@ -18,12 +18,17 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/minikube/pkg/perf/monitor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for {
|
||||
log.Print("~~~~~~~~~ Starting performance analysis ~~~~~~~~~~~~~~")
|
||||
if err := analyzePerformance(context.Background()); err != nil {
|
||||
log.Printf("error executing performance analysis: %v", err)
|
||||
}
|
||||
|
|
@ -36,5 +41,32 @@ func main() {
|
|||
// 2. running mkcmp against those PRs
|
||||
// 3. commenting results on those PRs
|
||||
func analyzePerformance(ctx context.Context) error {
|
||||
client := monitor.NewClient(ctx, monitor.GithubOwner, monitor.GithubRepo)
|
||||
prs, err := client.ListOpenPRsWithLabel(monitor.OkToTestLabel)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "listing open prs")
|
||||
}
|
||||
log.Print("got prs:", prs)
|
||||
for _, pr := range prs {
|
||||
log.Printf("~~~ Analyzing PR %d ~~~", pr)
|
||||
newCommitsExist, err := client.NewCommitsExist(pr, monitor.BotName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !newCommitsExist {
|
||||
log.Println("New commits don't exist, skipping rerun...")
|
||||
continue
|
||||
}
|
||||
var message string
|
||||
message, err = monitor.RunMkcmp(ctx, pr)
|
||||
if err != nil {
|
||||
message = fmt.Sprintf("Error: %v\n%s", err, message)
|
||||
}
|
||||
log.Printf("message for pr %d:\n%s\n", pr, message)
|
||||
if err := client.CommentOnPR(pr, message); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Print("successfully commented on PR")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="702px" height="683px" viewBox="0 0 702 683" version="1.1" xmlns="https://www.w3.org/2000/svg" xmlns:xlink="https://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 39.1 (31720) - https://www.sketchapp.com/ -->
|
||||
<svg width="702px" height="683px" viewBox="0 0 702 683" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 39.1 (31720) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>minikube</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 8.1 KiB |
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/docker/machine/libmachine/ssh"
|
||||
"github.com/golang/glog"
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
// This file is for common code shared among internal machine drivers
|
||||
|
|
@ -74,7 +75,7 @@ func createRawDiskImage(sshKeyPath, diskPath string, diskSizeMb int) error {
|
|||
return errors.Wrapf(err, "closing file %s", diskPath)
|
||||
}
|
||||
|
||||
if err := os.Truncate(diskPath, int64(diskSizeMb*1000000)); err != nil {
|
||||
if err := os.Truncate(diskPath, util.ConvertMBToBytes(diskSizeMb)); err != nil {
|
||||
return errors.Wrap(err, "truncate")
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ func Test_createDiskImage(t *testing.T) {
|
|||
diskPath := filepath.Join(tmpdir, "disk")
|
||||
|
||||
sizeInMb := 100
|
||||
sizeInBytes := int64(sizeInMb) * 1000000
|
||||
sizeInBytes := int64(104857600)
|
||||
if err := createRawDiskImage(sshPath, diskPath, sizeInMb); err != nil {
|
||||
t.Errorf("createDiskImage() error = %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ import (
|
|||
|
||||
const domainTmpl = `
|
||||
<domain type='kvm'>
|
||||
<name>{{.MachineName}}</name>
|
||||
<memory unit='MB'>{{.Memory}}</memory>
|
||||
<name>{{.MachineName}}</name>
|
||||
<memory unit='MiB'>{{.Memory}}</memory>
|
||||
<vcpu>{{.CPU}}</vcpu>
|
||||
<features>
|
||||
<acpi/>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import (
|
|||
"k8s.io/minikube/pkg/minikube/out"
|
||||
"k8s.io/minikube/pkg/minikube/out/register"
|
||||
"k8s.io/minikube/pkg/minikube/style"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
// HostInfo holds information on the user's machine
|
||||
|
|
@ -38,10 +39,6 @@ type HostInfo struct {
|
|||
DiskSize int64
|
||||
}
|
||||
|
||||
func megs(bytes uint64) int64 {
|
||||
return int64(bytes / 1024 / 1024)
|
||||
}
|
||||
|
||||
// CachedHostInfo returns system information such as memory,CPU, DiskSize
|
||||
func CachedHostInfo() (*HostInfo, error, error, error) {
|
||||
var cpuErr, memErr, diskErr error
|
||||
|
|
@ -61,8 +58,8 @@ func CachedHostInfo() (*HostInfo, error, error, error) {
|
|||
|
||||
var info HostInfo
|
||||
info.CPUs = len(i)
|
||||
info.Memory = megs(v.Total)
|
||||
info.DiskSize = megs(d.Total)
|
||||
info.Memory = util.ConvertUnsignedBytesToMB(v.Total)
|
||||
info.DiskSize = util.ConvertUnsignedBytesToMB(d.Total)
|
||||
return &info, cpuErr, memErr, diskErr
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ func TestTimeCommandLogs(t *testing.T) {
|
|||
if !ok {
|
||||
t.Fatalf("expected log %s but didn't find it", log)
|
||||
}
|
||||
if actualTime < time {
|
||||
// Let's give a little wiggle room so we don't fail if time is 3 and actualTime is 2.999
|
||||
if actualTime < time && time-actualTime > 0.001 {
|
||||
t.Fatalf("expected log \"%s\" to take more time than it actually did. got %v, expected > %v", log, actualTime, time)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
Copyright 2020 The Kubernetes Authors 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 monitor
|
||||
|
||||
const (
|
||||
GithubAccessTokenEnvVar = "GITHUB_ACCESS_TOKEN"
|
||||
OkToTestLabel = "ok-to-test"
|
||||
GithubOwner = "kubernetes"
|
||||
GithubRepo = "minikube"
|
||||
BotName = "minikube-pr-bot"
|
||||
)
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
Copyright 2020 The Kubernetes Authors 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 monitor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// RunMkcmp runs minikube built at the given pr against minikube at master
|
||||
func RunMkcmp(ctx context.Context, pr int) (string, error) {
|
||||
// run 'git pull' so that minikube dir is up to date
|
||||
if _, err := runCmdInMinikube(ctx, []string{"git", "pull", "origin", "master"}); err != nil {
|
||||
return "", errors.Wrap(err, "running git pull")
|
||||
}
|
||||
mkcmpPath := "out/mkcmp"
|
||||
minikubePath := "out/minikube"
|
||||
if _, err := runCmdInMinikube(ctx, []string{"make", mkcmpPath, minikubePath}); err != nil {
|
||||
return "", errors.Wrap(err, "building minikube and mkcmp at head")
|
||||
}
|
||||
return runCmdInMinikube(ctx, []string{mkcmpPath, minikubePath, fmt.Sprintf("pr://%d", pr)})
|
||||
}
|
||||
|
||||
// runCmdInMinikube runs the cmd and return stdout
|
||||
func runCmdInMinikube(ctx context.Context, command []string) (string, error) {
|
||||
cmd := exec.CommandContext(ctx, command[0], command[1:]...)
|
||||
cmd.Dir = minikubeDir()
|
||||
cmd.Env = os.Environ()
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd.Stdout = buf
|
||||
|
||||
log.Printf("Running: %v", cmd.Args)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return "", errors.Wrapf(err, "running %v: %v", cmd.Args, buf.String())
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func minikubeDir() string {
|
||||
return filepath.Join(os.Getenv("HOME"), "minikube")
|
||||
}
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
Copyright 2020 The Kubernetes Authors 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 monitor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-github/github"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// Client provides the context and client with necessary auth
|
||||
// for interacting with the Github API
|
||||
type Client struct {
|
||||
ctx context.Context
|
||||
*github.Client
|
||||
owner string
|
||||
repo string
|
||||
}
|
||||
|
||||
// NewClient returns a github client with the necessary auth
|
||||
func NewClient(ctx context.Context, owner, repo string) *Client {
|
||||
githubToken := os.Getenv(GithubAccessTokenEnvVar)
|
||||
// Setup the token for github authentication
|
||||
ts := oauth2.StaticTokenSource(
|
||||
&oauth2.Token{AccessToken: githubToken},
|
||||
)
|
||||
tc := oauth2.NewClient(context.Background(), ts)
|
||||
|
||||
// Return a client instance from github
|
||||
client := github.NewClient(tc)
|
||||
return &Client{
|
||||
ctx: ctx,
|
||||
Client: client,
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
// CommentOnPR comments message on the PR
|
||||
func (g *Client) CommentOnPR(pr int, message string) error {
|
||||
comment := &github.IssueComment{
|
||||
Body: &message,
|
||||
}
|
||||
|
||||
log.Printf("Creating comment on PR %d: %s", pr, message)
|
||||
_, _, err := g.Client.Issues.CreateComment(g.ctx, g.owner, g.repo, pr, comment)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "creating github comment")
|
||||
}
|
||||
log.Printf("Successfully commented on PR %d.", pr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListOpenPRsWithLabel returns all open PRs with the specified label
|
||||
func (g *Client) ListOpenPRsWithLabel(label string) ([]int, error) {
|
||||
validPrs := []int{}
|
||||
prs, _, err := g.Client.PullRequests.List(g.ctx, g.owner, g.repo, &github.PullRequestListOptions{})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "listing pull requests")
|
||||
}
|
||||
for _, pr := range prs {
|
||||
if prContainsLabel(pr.Labels, label) {
|
||||
validPrs = append(validPrs, pr.GetNumber())
|
||||
}
|
||||
}
|
||||
return validPrs, nil
|
||||
}
|
||||
|
||||
func prContainsLabel(labels []*github.Label, label string) bool {
|
||||
for _, l := range labels {
|
||||
if l == nil {
|
||||
continue
|
||||
}
|
||||
if l.GetName() == label {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NewCommitsExist checks if new commits exist since minikube-pr-bot
|
||||
// commented on the PR. If so, return true.
|
||||
func (g *Client) NewCommitsExist(pr int, login string) (bool, error) {
|
||||
lastCommentTime, err := g.timeOfLastComment(pr, login)
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "getting time of last comment by %s on pr %d", login, pr)
|
||||
}
|
||||
lastCommitTime, err := g.timeOfLastCommit(pr)
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "getting time of last commit on pr %d", pr)
|
||||
}
|
||||
return lastCommentTime.Before(lastCommitTime), nil
|
||||
}
|
||||
|
||||
func (g *Client) timeOfLastCommit(pr int) (time.Time, error) {
|
||||
var commits []*github.RepositoryCommit
|
||||
|
||||
page := 0
|
||||
resultsPerPage := 30
|
||||
for {
|
||||
c, _, err := g.Client.PullRequests.ListCommits(g.ctx, g.owner, g.repo, pr, &github.ListOptions{
|
||||
Page: page,
|
||||
PerPage: resultsPerPage,
|
||||
})
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
commits = append(commits, c...)
|
||||
if len(c) < resultsPerPage {
|
||||
break
|
||||
}
|
||||
page++
|
||||
}
|
||||
|
||||
lastCommitTime := time.Time{}
|
||||
for _, c := range commits {
|
||||
if newCommitTime := c.GetCommit().GetAuthor().GetDate(); newCommitTime.After(lastCommitTime) {
|
||||
lastCommitTime = newCommitTime
|
||||
}
|
||||
}
|
||||
return lastCommitTime, nil
|
||||
}
|
||||
|
||||
func (g *Client) timeOfLastComment(pr int, login string) (time.Time, error) {
|
||||
var comments []*github.IssueComment
|
||||
|
||||
page := 0
|
||||
resultsPerPage := 30
|
||||
for {
|
||||
c, _, err := g.Client.Issues.ListComments(g.ctx, g.owner, g.repo, pr, &github.IssueListCommentsOptions{
|
||||
ListOptions: github.ListOptions{
|
||||
Page: page,
|
||||
PerPage: resultsPerPage,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
comments = append(comments, c...)
|
||||
if len(c) < resultsPerPage {
|
||||
break
|
||||
}
|
||||
page++
|
||||
}
|
||||
|
||||
// go through comments backwards to find the most recent
|
||||
lastCommentTime := time.Time{}
|
||||
|
||||
for _, c := range comments {
|
||||
if u := c.GetUser(); u != nil {
|
||||
if u.GetLogin() == login {
|
||||
if c.GetCreatedAt().After(lastCommentTime) {
|
||||
lastCommentTime = c.GetCreatedAt()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lastCommentTime, nil
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ import (
|
|||
"strconv"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/docker/go-units"
|
||||
units "github.com/docker/go-units"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
|
@ -47,6 +47,18 @@ func CalculateSizeInMB(humanReadableSize string) (int, error) {
|
|||
return int(size / units.MiB), nil
|
||||
}
|
||||
|
||||
func ConvertMBToBytes(mbSize int) int64 {
|
||||
return int64(mbSize) * units.MiB
|
||||
}
|
||||
|
||||
func ConvertBytesToMB(byteSize int64) int {
|
||||
return int(ConvertUnsignedBytesToMB(uint64(byteSize)))
|
||||
}
|
||||
|
||||
func ConvertUnsignedBytesToMB(byteSize uint64) int64 {
|
||||
return int64(byteSize / units.MiB)
|
||||
}
|
||||
|
||||
// GetBinaryDownloadURL returns a suitable URL for the platform
|
||||
func GetBinaryDownloadURL(version, platform string) string {
|
||||
switch platform {
|
||||
|
|
|
|||
Loading…
Reference in New Issue