diff --git a/pkg/common/common.go b/pkg/common/common.go
new file mode 100644
index 0000000000..b2cde38bdc
--- /dev/null
+++ b/pkg/common/common.go
@@ -0,0 +1,26 @@
+// Copyright 2019 Copyright (c) 2019 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file.
+//
+// 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 common
+
+const (
+ DockerImageGardenerApiServer = "eu.gcr.io/gardener-project/gardener/apiserver"
+)
+
+// Repositories
+const (
+ TestInfraRepo = "https://github.com/gardener/test-infra.git"
+ GardenSetupRepo = "https://github.com/schrodit/garden-setup.git"
+ GardenerRepo = "https://github.com/gardener/gardener.git"
+)
diff --git a/pkg/testrunner/renderer/templates/templates.go b/pkg/testrunner/renderer/templates/templates.go
index 952db3a647..73283c8db8 100644
--- a/pkg/testrunner/renderer/templates/templates.go
+++ b/pkg/testrunner/renderer/templates/templates.go
@@ -16,15 +16,10 @@ package templates
import (
"github.com/gardener/test-infra/pkg/apis/testmachinery/v1beta1"
+ "github.com/gardener/test-infra/pkg/common"
"github.com/gardener/test-infra/pkg/hostscheduler"
)
-const (
- TestInfraRepo = "https://github.com/gardener/test-infra.git"
- GardenSetupRepo = "https://github.com/schrodit/garden-setup.git"
- GardenerRepo = "https://github.com/gardener/gardener.git"
-)
-
var TestInfraLocationName = "tm"
var DefaultLocationSetName = "default"
@@ -34,7 +29,7 @@ var TestInfraLocation = v1beta1.LocationSet{
Locations: []v1beta1.TestLocation{
{
Type: v1beta1.LocationTypeGit,
- Repo: TestInfraRepo,
+ Repo: common.TestInfraRepo,
Revision: "master",
},
},
@@ -51,7 +46,7 @@ func GetDefaultLocationsSet(cfg GardenerConfig) v1beta1.LocationSet {
set.Locations = []v1beta1.TestLocation{
{
Type: v1beta1.LocationTypeGit,
- Repo: GardenerRepo,
+ Repo: common.GardenerRepo,
Revision: cfg.Version,
},
}
@@ -60,7 +55,7 @@ func GetDefaultLocationsSet(cfg GardenerConfig) v1beta1.LocationSet {
set.Locations = []v1beta1.TestLocation{
{
Type: v1beta1.LocationTypeGit,
- Repo: GardenerRepo,
+ Repo: common.GardenerRepo,
Revision: cfg.Commit,
},
}
@@ -77,7 +72,7 @@ func GetGardenSetupLocation(name, revision string) v1beta1.LocationSet {
Locations: []v1beta1.TestLocation{
{
Type: v1beta1.LocationTypeGit,
- Repo: GardenSetupRepo,
+ Repo: common.GardenSetupRepo,
Revision: revision,
},
},
diff --git a/pkg/tm-bot/github/client.go b/pkg/tm-bot/github/client.go
index b68fc133df..ea27136b78 100644
--- a/pkg/tm-bot/github/client.go
+++ b/pkg/tm-bot/github/client.go
@@ -56,7 +56,11 @@ func (c *client) ResolveConfigValue(event *GenericRequestEvent, value *ghval.Git
return pr.GetHead().GetSHA(), nil
}
if value.Path != nil {
- file, dir, _, err := c.client.Repositories.GetContents(context.TODO(), event.GetOwnerName(), event.GetRepositoryName(), *value.Path, &github.RepositoryContentGetOptions{Ref: event.Repository.GetDefaultBranch()})
+ pr, _, err := c.client.PullRequests.Get(context.TODO(), event.GetOwnerName(), event.GetRepositoryName(), event.Number)
+ if err != nil {
+ return "", err
+ }
+ file, dir, _, err := c.client.Repositories.GetContents(context.TODO(), event.GetOwnerName(), event.GetRepositoryName(), *value.Path, &github.RepositoryContentGetOptions{Ref: pr.GetHead().GetSHA()})
if err != nil {
return "", err
}
diff --git a/pkg/tm-bot/plugins/errors/errors.go b/pkg/tm-bot/plugins/errors/errors.go
index 865132de3b..190d7443d9 100644
--- a/pkg/tm-bot/plugins/errors/errors.go
+++ b/pkg/tm-bot/plugins/errors/errors.go
@@ -74,5 +74,14 @@ func ShortForError(err error) string {
case *PluginError:
return t.short
}
- return "Unkown error"
+ return "Unknown error"
+}
+
+// LongForError returns long message for the error
+func LongForError(err error) string {
+ switch t := err.(type) {
+ case *PluginError:
+ return t.long
+ }
+ return "Unknown error"
}
diff --git a/pkg/tm-bot/plugins/request.go b/pkg/tm-bot/plugins/request.go
index bf93ccecbb..33d33e3f1c 100644
--- a/pkg/tm-bot/plugins/request.go
+++ b/pkg/tm-bot/plugins/request.go
@@ -30,32 +30,32 @@ func HandleRequest(client github.Client, event *github.GenericRequestEvent) erro
}
for _, args := range commands {
- go runPlugin(client, event, args)
+ go Plugins.runPlugin(client, event, args)
}
return nil
}
// runPlugin runs a plugin with a event and its arguments
-func runPlugin(client github.Client, event *github.GenericRequestEvent, args []string) {
- runID, plugin, err := Plugins.Get(args[0])
+func (p *plugins) runPlugin(client github.Client, event *github.GenericRequestEvent, args []string) {
+ runID, plugin, err := p.Get(args[0])
if err != nil {
- _ = Error(client, event, err)
+ _ = p.Error(client, event, nil, err)
return
}
- Plugins.initState(plugin, runID, event)
+ p.initState(plugin, runID, event)
fs := plugin.Flags()
if err := fs.Parse(args[1:]); err != nil {
- Plugins.RemoveState(plugin, runID)
- _ = Error(client, event, pluginerr.New(err.Error(), FormatUsageError(args[0], plugin.Description(), plugin.Example(), fs.FlagUsages())))
+ p.RemoveState(plugin, runID)
+ _ = p.Error(client, event, plugin, pluginerr.New(err.Error(), "unable to parse flags"))
return
}
if err := plugin.Run(fs, client, event); err != nil {
if !pluginerr.IsRecoverable(err) {
Plugins.RemoveState(plugin, runID)
- _ = Error(client, event, pluginerr.New(err.Error(), FormatUsageError(args[0], plugin.Description(), plugin.Example(), fs.FlagUsages())))
+ _ = p.Error(client, event, plugin, err)
}
return
}
@@ -71,7 +71,7 @@ func (p *plugins) resumePlugin(ghMgr github.Manager, name, runID string, state *
return
}
- _, plugin, err := Plugins.Get(name)
+ _, plugin, err := p.Get(name)
if err != nil {
p.log.Error(err, "unable to get plugin for state", "plugin", name)
return
@@ -80,8 +80,8 @@ func (p *plugins) resumePlugin(ghMgr github.Manager, name, runID string, state *
if err := plugin.ResumeFromState(ghClient, state.Event, state.Custom); err != nil {
if !pluginerr.IsRecoverable(err) {
- Plugins.RemoveState(plugin, runID)
- _ = Error(ghClient, state.Event, pluginerr.New(err.Error(), FormatUsageError(name, plugin.Description(), plugin.Example(), plugin.Flags().FlagUsages())))
+ p.RemoveState(plugin, runID)
+ _ = p.Error(ghClient, state.Event, plugin, err)
}
return
}
@@ -89,11 +89,15 @@ func (p *plugins) resumePlugin(ghMgr github.Manager, name, runID string, state *
}
// Error responds to the client if an error occurs
-func Error(client github.Client, event *github.GenericRequestEvent, err error) error {
- if _, err := client.Comment(event, FormatErrorResponse(event.GetAuthorName(), pluginerr.ShortForError(err), err.Error())); err != nil {
+func (p *plugins) Error(client github.Client, event *github.GenericRequestEvent, plugin Plugin, err error) error {
+ p.log.Error(err, err.Error())
+
+ if plugin != nil {
+ _, err := client.Comment(event, FormatErrorResponse(event.GetAuthorName(), pluginerr.ShortForError(err), FormatUsageError(plugin.Command(), plugin.Description(), plugin.Example(), plugin.Flags().FlagUsages())))
return err
}
- return nil
+ _, err = client.Comment(event, FormatSimpleErrorResponse(event.GetAuthorName(), pluginerr.ShortForError(err)))
+ return err
}
// ParseCommands parses a message and returns a string of commands and arguments
diff --git a/pkg/tm-bot/plugins/respond.go b/pkg/tm-bot/plugins/respond.go
index e09eb771a0..48c2e3fe48 100644
--- a/pkg/tm-bot/plugins/respond.go
+++ b/pkg/tm-bot/plugins/respond.go
@@ -45,16 +45,28 @@ func FormatResponseWithReason(to, message, reason string) string {
return fmt.Sprintf(format, to, message, reason, AboutThisBotWithoutCommands)
}
+// FormatSimpleErrorResponse formats a response that does not warrant additional explanation in the
+// details section.
+func FormatSimpleErrorResponse(to, message string) string {
+ format := `:fire: Oops, something went wrong @%s
+%s
+
+>%s`
+
+ return fmt.Sprintf(format, to, message, AboutThisBotWithoutCommands)
+}
+
// FormatErrorResponse formats a response that does not warrant additional explanation in the
// details section.
func FormatErrorResponse(to, message, reason string) string {
- format := `:fire: Oops, something went wrong
-@%s: %s
+ format := `:fire: Oops, something went wrong @%s
+%s
%s
-%s
- `
+
+
+>%s`
return fmt.Sprintf(format, to, message, reason, AboutThisBotWithoutCommands)
}
diff --git a/pkg/tm-bot/plugins/tests/flags.go b/pkg/tm-bot/plugins/tests/flags.go
index f5087d9632..a4f2bc2311 100644
--- a/pkg/tm-bot/plugins/tests/flags.go
+++ b/pkg/tm-bot/plugins/tests/flags.go
@@ -15,10 +15,14 @@
package tests
import (
+ "fmt"
"github.com/gardener/gardener/pkg/apis/garden/v1beta1"
+ "github.com/gardener/test-infra/pkg/common"
"github.com/gardener/test-infra/pkg/hostscheduler/gardenerscheduler"
"github.com/gardener/test-infra/pkg/testmachinery"
"github.com/gardener/test-infra/pkg/tm-bot/github"
+ "github.com/gardener/test-infra/pkg/tm-bot/plugins/errors"
+ "github.com/gardener/test-infra/pkg/util"
"github.com/gardener/test-infra/pkg/util/cmdvalues"
"github.com/ghodss/yaml"
"github.com/spf13/pflag"
@@ -34,7 +38,13 @@ const (
cloudprovider = "cloudprovider"
)
-func (t *test) ValidateFlags(flagset *pflag.FlagSet) error {
+func (t *test) ValidateConfig() error {
+ if t.config.Gardener.Version != "" {
+ if err := util.CheckDockerImageExists(common.DockerImageGardenerApiServer, t.config.Gardener.Version); err != nil {
+ return errors.New(fmt.Sprintf("I am unable to find gardener images of version %s.\n Have you specified the right version?", t.config.Gardener.Version),
+ "Maybe you should run the default gardener pipeline before trying to run the integration tests.")
+ }
+ }
return nil
}
@@ -61,16 +71,16 @@ func (t *test) Flags() *pflag.FlagSet {
return flagset
}
-func (t *test) ApplyDefaultConfig(client github.Client, event *github.GenericRequestEvent, flagset *pflag.FlagSet) {
+func (t *test) ApplyDefaultConfig(client github.Client, event *github.GenericRequestEvent, flagset *pflag.FlagSet) error {
raw, err := client.GetConfig(t.Command())
if err != nil {
t.log.Error(err, "cannot get default config")
- return
+ return nil
}
var defaultConfig DefaultsConfig
if err := yaml.Unmarshal(raw, &defaultConfig); err != nil {
t.log.Error(err, "unable to parse default config")
- return
+ return errors.New("unable to parse default config", err.Error())
}
if !flagset.Changed(hostprovider) && defaultConfig.HostProvider != nil {
@@ -82,7 +92,7 @@ func (t *test) ApplyDefaultConfig(client github.Client, event *github.GenericReq
if !flagset.Changed(gardensetupRevision) && defaultConfig.GardenSetup != nil && defaultConfig.GardenSetup.Revision != nil {
val, err := client.ResolveConfigValue(event, defaultConfig.GardenSetup.Revision.Value())
if err != nil {
- t.log.Error(err, "unable to resolve config value for garden setup revision")
+ return errors.New("unable to resolve default config value for garden setup revision", err.Error())
} else {
t.config.GardenSetupRevision = val
}
@@ -90,7 +100,7 @@ func (t *test) ApplyDefaultConfig(client github.Client, event *github.GenericReq
if !flagset.Changed(gardenerVersion) && defaultConfig.Gardener != nil && defaultConfig.Gardener.Version != nil {
val, err := client.ResolveConfigValue(event, defaultConfig.Gardener.Version.Value())
if err != nil {
- t.log.Error(err, "unable to resolve config value for gardener version")
+ return errors.New("unable to resolve default config value for gardener version", err.Error())
} else {
t.config.Gardener.Version = val
}
@@ -98,7 +108,7 @@ func (t *test) ApplyDefaultConfig(client github.Client, event *github.GenericReq
if !flagset.Changed(gardenerCommit) && defaultConfig.Gardener != nil && defaultConfig.Gardener.Commit != nil {
val, err := client.ResolveConfigValue(event, defaultConfig.Gardener.Commit.Value())
if err != nil {
- t.log.Error(err, "unable to resolve config value for gardener commit")
+ return errors.New("unable to resolve default config value for gardener commit", err.Error())
} else {
t.config.Gardener.Commit = val
}
@@ -110,4 +120,5 @@ func (t *test) ApplyDefaultConfig(client github.Client, event *github.GenericReq
if !flagset.Changed(cloudprovider) && defaultConfig.CloudProviders != nil {
t.config.Shoots.CloudProviders = *defaultConfig.CloudProviders
}
+ return nil
}
diff --git a/pkg/tm-bot/plugins/tests/run.go b/pkg/tm-bot/plugins/tests/run.go
index 0ef5723738..416b5e92e2 100644
--- a/pkg/tm-bot/plugins/tests/run.go
+++ b/pkg/tm-bot/plugins/tests/run.go
@@ -38,7 +38,12 @@ func (t *test) Run(flagset *pflag.FlagSet, client github.Client, event *github.G
ctx := context.Background()
defer ctx.Done()
- t.ApplyDefaultConfig(client, event, flagset)
+ if err := t.ApplyDefaultConfig(client, event, flagset); err != nil {
+ return err
+ }
+ if err := t.ValidateConfig(); err != nil {
+ return err
+ }
t.config.Shoots.DefaultTest = templates.TestWithLabels(t.testLabel)
if t.hibernation {
diff --git a/pkg/util/docker_image.go b/pkg/util/docker_image.go
new file mode 100644
index 0000000000..cbebc6f994
--- /dev/null
+++ b/pkg/util/docker_image.go
@@ -0,0 +1,89 @@
+// Copyright 2019 Copyright (c) 2019 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file.
+//
+// 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 util
+
+import (
+ "encoding/json"
+ "github.com/pkg/errors"
+ "net/http"
+ "net/url"
+ "strings"
+)
+
+type dockerImages struct {
+ Tags []string `json:"tags"`
+}
+
+// CheckDockerImageExists checks if a docker image exists
+func CheckDockerImageExists(image, tag string) error {
+
+ // Build hostname/v2//manifests/ to directly check if the image exists
+ splitImage := strings.Split(image, "/")
+ tail := splitImage[1:]
+ reqPath := append(append([]string{"v2"}, tail...), "manifests", tag)
+
+ u := &url.URL{
+ Scheme: "https",
+ Host: splitImage[0],
+ Path: strings.Join(reqPath, "/"),
+ }
+ res, err := http.Get(u.String())
+ if err != nil {
+ return err
+ }
+ if res.StatusCode != http.StatusOK {
+ return errors.New("tag does not exist")
+ }
+ return nil
+}
+
+// GetDockerImageFromCommit searches all tags of a image and try to matches the commit (e.g. .10.0-dev-).
+// The image tag is returned if an applicable tag can be found
+// todo: use pagination
+func GetDockerImageFromCommit(image, commit string) (string, error) {
+
+ // construct api call with the form hostname/v2//tags/list
+ splitImage := strings.Split(image, "/")
+ tail := splitImage[1:]
+ reqPath := append(append([]string{"v2"}, tail...), "tags", "list")
+
+ u := &url.URL{
+ Scheme: "https",
+ Host: splitImage[0],
+ Path: strings.Join(reqPath, "/"),
+ }
+ res, err := http.Get(u.String())
+ if err != nil {
+ return "", err
+ }
+ if res.StatusCode != http.StatusOK {
+ return "", errors.New("no tag found")
+ }
+ defer res.Body.Close()
+
+ decoder := json.NewDecoder(res.Body)
+ var images dockerImages
+ if err := decoder.Decode(&images); err != nil {
+ return "", err
+ }
+
+ for _, tag := range images.Tags {
+ if strings.Contains(tag, commit) {
+ return tag, nil
+ }
+ }
+
+ return "", errors.New("no tag found")
+}