github.com/abayer/test-infra@v0.0.5/prow/plugins/dog/dog.go (about)

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package dog adds dog images to issues in response to a /woof comment
    18  package dog
    19  
    20  import (
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"net/http"
    25  	"net/url"
    26  	"regexp"
    27  
    28  	"github.com/sirupsen/logrus"
    29  
    30  	"k8s.io/test-infra/prow/github"
    31  	"k8s.io/test-infra/prow/pluginhelp"
    32  	"k8s.io/test-infra/prow/plugins"
    33  )
    34  
    35  var (
    36  	match     = regexp.MustCompile(`(?mi)^/(woof|bark)\s*$`)
    37  	filetypes = regexp.MustCompile(`(?i)\.(jpg|gif|png)$`)
    38  )
    39  
    40  const (
    41  	dogURL     = realPack("https://random.dog/woof.json")
    42  	pluginName = "dog"
    43  )
    44  
    45  func init() {
    46  	plugins.RegisterGenericCommentHandler(pluginName, handleGenericComment, helpProvider)
    47  }
    48  
    49  func helpProvider(config *plugins.Configuration, enabledRepos []string) (*pluginhelp.PluginHelp, error) {
    50  	// The Config field is omitted because this plugin is not configurable.
    51  	pluginHelp := &pluginhelp.PluginHelp{
    52  		Description: "The dog plugin adds a dog image to an issue in response to the `/woof` command.",
    53  	}
    54  	pluginHelp.AddCommand(pluginhelp.Command{
    55  		Usage:       "/(woof|bark)",
    56  		Description: "Add a dog image to the issue",
    57  		Featured:    false,
    58  		WhoCanUse:   "Anyone",
    59  		Examples:    []string{"/woof", "/bark"},
    60  	})
    61  	return pluginHelp, nil
    62  }
    63  
    64  type githubClient interface {
    65  	CreateComment(owner, repo string, number int, comment string) error
    66  }
    67  
    68  type pack interface {
    69  	readDog() (string, error)
    70  }
    71  
    72  type realPack string
    73  
    74  var client = http.Client{}
    75  
    76  type dogResult struct {
    77  	URL string `json:"url"`
    78  }
    79  
    80  func (dr dogResult) Format() (string, error) {
    81  	if dr.URL == "" {
    82  		return "", errors.New("empty url")
    83  	}
    84  	src, err := url.ParseRequestURI(dr.URL)
    85  	if err != nil {
    86  		return "", fmt.Errorf("invalid url %s: %v", dr.URL, err)
    87  	}
    88  	return fmt.Sprintf("[![dog image](%s)](%s)", src, src), nil
    89  }
    90  
    91  func (u realPack) readDog() (string, error) {
    92  	uri := string(u)
    93  	req, err := http.NewRequest("GET", uri, nil)
    94  	if err != nil {
    95  		return "", fmt.Errorf("could not create request %s: %v", uri, err)
    96  	}
    97  	req.Header.Add("Accept", "application/json")
    98  	resp, err := client.Do(req)
    99  	if err != nil {
   100  		return "", fmt.Errorf("could not read dog from %s: %v", uri, err)
   101  	}
   102  	defer resp.Body.Close()
   103  	var a dogResult
   104  	if err = json.NewDecoder(resp.Body).Decode(&a); err != nil {
   105  		return "", err
   106  	}
   107  	// GitHub doesn't support videos :(
   108  	if !filetypes.MatchString(a.URL) {
   109  		return "", errors.New("unsupported doggo :( unknown filetype: " + a.URL)
   110  	}
   111  	// checking size, GitHub doesn't support big images
   112  	toobig, err := github.ImageTooBig(a.URL)
   113  	if err != nil {
   114  		return "", err
   115  	} else if toobig {
   116  		return "", errors.New("unsupported doggo :( size too big: " + a.URL)
   117  	}
   118  	return a.Format()
   119  }
   120  
   121  func handleGenericComment(pc plugins.PluginClient, e github.GenericCommentEvent) error {
   122  	return handle(pc.GitHubClient, pc.Logger, &e, dogURL)
   123  }
   124  
   125  func handle(gc githubClient, log *logrus.Entry, e *github.GenericCommentEvent, p pack) error {
   126  	// Only consider new comments.
   127  	if e.Action != github.GenericCommentActionCreated {
   128  		return nil
   129  	}
   130  	// Make sure they are requesting a dog
   131  	mat := match.FindStringSubmatch(e.Body)
   132  	if mat == nil {
   133  		return nil
   134  	}
   135  
   136  	org := e.Repo.Owner.Login
   137  	repo := e.Repo.Name
   138  	number := e.Number
   139  
   140  	for i := 0; i < 5; i++ {
   141  		resp, err := p.readDog()
   142  		if err != nil {
   143  			log.WithError(err).Println("Failed to get dog img")
   144  			continue
   145  		}
   146  		return gc.CreateComment(org, repo, number, plugins.FormatResponseRaw(e.Body, e.HTMLURL, e.User.Login, resp))
   147  	}
   148  
   149  	return errors.New("could not find a valid dog image")
   150  }