github.com/jenkins-x/jx/v2@v2.1.155/pkg/jenkins/update_center.go (about)

     1  package jenkins
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"sort"
     8  	"strings"
     9  	"unicode/utf8"
    10  
    11  	"github.com/jenkins-x/jx-logging/pkg/log"
    12  	"github.com/jenkins-x/jx/v2/pkg/util"
    13  	"github.com/pkg/errors"
    14  )
    15  
    16  const (
    17  	// DefaultUpdateCenterURL the LTS update center metadata URL
    18  	DefaultUpdateCenterURL = "https://updates.jenkins.io/current/update-center.json"
    19  )
    20  
    21  // CoreVersion represents the version of Core
    22  type CoreVersion struct {
    23  	BuildDate string `json:"buildDate"`
    24  	Name      string `json:"name"`
    25  	Sha1      string `json:"sha1"`
    26  	Sha256    string `json:"sha256"`
    27  	URL       string `json:"url"`
    28  	Version   string `json:"version"`
    29  }
    30  
    31  // Dependency a dependency of a plugin
    32  type Dependency struct {
    33  	Name     string `json:"name"`
    34  	Optional bool   `json:"optional"`
    35  	Version  string `json:"version"`
    36  }
    37  
    38  // Developer a developer on a plugin
    39  type Developer struct {
    40  	DeveloperID string `json:"developerId"`
    41  	Email       string `json:"email"`
    42  	Name        string `json:"name"`
    43  }
    44  
    45  // PluginRelease represets the data for a plugin
    46  type PluginRelease struct {
    47  	BuildDate    string       `json:"buildDate"`
    48  	Dependencies []Dependency `json:"dependencies"`
    49  
    50  	Developers        []Developer `json:"developers"`
    51  	Excerpt           string      `json:"excerpt"`
    52  	Gav               string      `json:"gav"`
    53  	Labels            []string    `json:"labels"`
    54  	Name              string      `json:"name"`
    55  	PreviousTimestamp string      `json:"previousTimestamp"`
    56  	PreviousVersion   string      `json:"previousVersion"`
    57  	ReleaseTimestamp  string      `json:"releaseTimestamp"`
    58  	RequiredCore      string      `json:"requiredCore"`
    59  	Scm               string      `json:"scm"`
    60  	Sha1              string      `json:"sha1"`
    61  	Sha256            string      `json:"sha256"`
    62  	Title             string      `json:"title"`
    63  	URL               string      `json:"url"`
    64  	Version           string      `json:"version"`
    65  	Wiki              string      `json:"wiki"`
    66  }
    67  
    68  // Signature signature metadata
    69  type Signature struct {
    70  	Certificates        []string `json:"certificates"`
    71  	CorrectDigest       string   `json:"correct_digest"`
    72  	CorrectDigest512    string   `json:"correct_digest512"`
    73  	CorrectSignature    string   `json:"correct_signature"`
    74  	CorrectSignature512 string   `json:"correct_signature512"`
    75  	Digest              string   `json:"digest"`
    76  	Digest512           string   `json:"digest512"`
    77  	Signature           string   `json:"signature"`
    78  	Signature512        string   `json:"signature512"`
    79  }
    80  
    81  // Warning a warning message
    82  type Warning struct {
    83  	ID       string           `json:"id"`
    84  	Message  string           `json:"message"`
    85  	Name     string           `json:"name"`
    86  	Type     string           `json:"type"`
    87  	URL      string           `json:"url"`
    88  	Versions []WarningVersion `json:"versions"`
    89  }
    90  
    91  // WarningVersion warning versions
    92  type WarningVersion struct {
    93  	FirstVersion string `json:"firstVersion"`
    94  	LastVersion  string `json:"lastVersion"`
    95  	Pattern      string `json:"pattern"`
    96  }
    97  
    98  // UpdateCenter represents the Update Center metadata returned from URLs
    99  // like https://updates.jenkins.io/current/update-center.json
   100  type UpdateCenter struct {
   101  	ConnectionCheckURL  string                   `json:"connectionCheckUrl"`
   102  	Core                CoreVersion              `json:"core"`
   103  	ID                  string                   `json:"id"`
   104  	Plugins             map[string]PluginRelease `json:"plugins"`
   105  	Signature           Signature                `json:"signature"`
   106  	UpdateCenterVersion string                   `json:"updateCenterVersion"`
   107  	Warnings            []Warning                `json:"warnings"`
   108  }
   109  
   110  // LoadUpdateCenterFile loads the given UpdateCenter JSON file
   111  func LoadUpdateCenterFile(fileName string) (*UpdateCenter, error) {
   112  	data, err := ioutil.ReadFile(fileName)
   113  	if err != nil {
   114  		return nil, errors.Wrapf(err, "failed to load UpdateCenter file %s", fileName)
   115  	}
   116  	answer, err := LoadUpdateCenterData(data)
   117  	if err != nil {
   118  		return answer, errors.Wrapf(err, "failed to unmarshal JSON from UpdateCenter file %s", fileName)
   119  	}
   120  	return answer, nil
   121  }
   122  
   123  // LoadUpdateCenterURL loads the given UpdateCenter URL
   124  func LoadUpdateCenterURL(u string) (*UpdateCenter, error) {
   125  	httpClient := util.GetClient()
   126  	resp, err := httpClient.Get(u)
   127  	if err != nil {
   128  		return nil, errors.Wrapf(err, "failed to invoke GET on %s", u)
   129  	}
   130  	stream := resp.Body
   131  	defer stream.Close()
   132  
   133  	data, err := ioutil.ReadAll(stream)
   134  	if err != nil {
   135  		return nil, errors.Wrapf(err, "failed to GET data from %s", u)
   136  	}
   137  	if resp.StatusCode >= 400 {
   138  		return nil, fmt.Errorf("status %s when performing GET on %s", resp.Status, u)
   139  	}
   140  	answer, err := LoadUpdateCenterData(data)
   141  	if err != nil {
   142  		return answer, errors.Wrapf(err, "failed to unmarshal JSON from UpdateCenter URL %s", u)
   143  	}
   144  	return answer, nil
   145  }
   146  
   147  // LoadUpdateCenterData loads the given UpdateCenter data
   148  func LoadUpdateCenterData(data []byte) (*UpdateCenter, error) {
   149  	answer := &UpdateCenter{}
   150  	// lets check for JSONP and strip it
   151  	text := strings.TrimSpace(string(data))
   152  	if !strings.HasPrefix(text, "{") && !strings.HasPrefix(text, "[") {
   153  		// we have JSONP of the form 'foo.bar(....)' so lets grab the contents
   154  		i := strings.Index(text, "(")
   155  		if i < 0 {
   156  			lines := strings.SplitN(text, "\n", 2)
   157  			return answer, fmt.Errorf("invalid JSON and JSONP document. Was expecting '(' but got: %s", lines[0])
   158  		}
   159  		text = text[i+1:]
   160  		text = strings.TrimSuffix(text, ";")
   161  		text = strings.TrimSpace(text)
   162  		text = strings.TrimSuffix(text, ")")
   163  		text = strings.TrimSpace(text)
   164  		data = []byte(text)
   165  	}
   166  	err := json.Unmarshal(data, answer)
   167  	return answer, err
   168  }
   169  
   170  // PickPlugins provides the user with a list of plugins that can be added to a Jenkins App
   171  func (u *UpdateCenter) PickPlugins(currentValues []string, handles util.IOFileHandles) ([]string, error) {
   172  	names := []string{}
   173  	pluginMap := map[string]string{}
   174  	maxLen := 1
   175  	for _, plugin := range u.Plugins {
   176  		l := utf8.RuneCountInString(plugin.Name)
   177  		if l > maxLen {
   178  			maxLen = l
   179  		}
   180  	}
   181  	defaults := []string{}
   182  
   183  	for _, plugin := range u.Plugins {
   184  		name := util.PadRight(plugin.Name, " ", maxLen) + " " + plugin.Title
   185  		namePrefix := plugin.Name + ":"
   186  		current := false
   187  		for _, cv := range currentValues {
   188  			if strings.HasPrefix(cv, namePrefix) {
   189  				current = true
   190  				break
   191  			}
   192  		}
   193  		if current {
   194  			defaults = append(defaults, name)
   195  		}
   196  		value := namePrefix + plugin.Version
   197  		names = append(names, name)
   198  		pluginMap[name] = value
   199  	}
   200  	sort.Strings(names)
   201  	help := "select the Jenkins plugins you wish to include inside your Jenkins App"
   202  	message := "pick Jenkins plugins to include in your Jenkins App: "
   203  	selection, err := util.PickNamesWithDefaults(names, defaults, message, help, handles)
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  	answer := []string{}
   208  	for _, sel := range selection {
   209  		value := pluginMap[sel]
   210  		if value == "" {
   211  			log.Logger().Warnf("Could not find value for %s in map!", value)
   212  		} else {
   213  			answer = append(answer, value)
   214  		}
   215  	}
   216  	return answer, nil
   217  }