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 }