Skip to content

Instantly share code, notes, and snippets.

@anthonydahanne
Last active July 7, 2023 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anthonydahanne/3b8977e04d500936dec7660cf3c354d2 to your computer and use it in GitHub Desktop.
Save anthonydahanne/3b8977e04d500936dec7660cf3c354d2 to your computer and use it in GitHub Desktop.
Update release numbers on pivnet
module pivnet/patcher
go 1.20
require gopkg.in/yaml.v3 v3.0.1
package main
import (
"bytes"
"encoding/json"
"fmt"
"gopkg.in/yaml.v3"
"io"
"log"
"net/http"
"os"
"regexp"
)
const pivnetApiUrl = "https://network.tanzu.vmware.com/api/v2/"
const pivnetProduct = "java-buildpack"
const stringToAppend = ".0"
const dotpivnetrc = ".pivnetrc"
type PivnetConf struct {
Profile []map[string]string `yaml:"profiles"`
}
func main() {
pattern := regexp.MustCompile("([\\d]+.[\\d]+.[\\d]+)")
dirname, err := os.UserHomeDir()
if err != nil {
log.Fatal("Impossible to get to user directory", err)
}
filename := dirname + "/" + dotpivnetrc
pivnetConfig, err := readConf(filename)
if err != nil {
log.Fatal("pivnet configuration was not loaded; please make sure you have it generated using pivnet login")
}
log.Printf("✅ Successfully loaded pivnet local configuration at %q", filename)
accessToken := pivnetConfig.Profile[0]["access_token"]
if accessToken == "" {
log.Fatal("pivnet configuration does not contain an api_token set; please make sure you have it generated using pivnet login")
}
log.Printf("✅ Successfully loaded pivnet access_token")
allReleasesResponse := map[string]interface{}{}
err = retrieveReleases(accessToken, allReleasesResponse)
if err != nil {
log.Fatal("impossible to retrieve releases from pivnet", err)
}
var releases = allReleasesResponse["releases"].([]interface{})
log.Printf("✅ Successfully retrieved %d releases for the TanzuNet product named %s", len(releases), pivnetProduct)
for _, release := range releases {
var currentRelease = release.(map[string]interface{})
id := int(currentRelease["id"].(float64))
version := currentRelease["version"].(string)
if pattern.MatchString(version) {
log.Printf("⏭️ skipping %s since it already matches the expected version schema, X.Y.Z", version)
continue
}
log.Printf("❓ OK to patch %s version %s (release id %d) into version %s ? y/n", pivnetProduct, version, id, version+stringToAppend)
var answer string
fmt.Scan(&answer)
if answer == "y" {
resultResponse := map[string]interface{}{}
err := patchRelease(accessToken, currentRelease, id, version, resultResponse)
if err != nil {
log.Print("impossible to patch this release - moving on.", err)
} else {
log.Print(resultResponse["message"])
}
} else {
continue
}
}
}
func patchRelease(accessToken string, currentRelease map[string]interface{}, id int, version string, resultResponse map[string]interface{}) error {
currentRelease["version"] = version + stringToAppend
jsonMarshalledRelease, err := json.Marshal(currentRelease)
if err != nil {
return err
}
req, err := http.NewRequest("PUT", pivnetApiUrl+"products/"+pivnetProduct+"/releases/"+fmt.Sprint(id), bytes.NewBuffer(jsonMarshalledRelease))
if err != nil {
return err
}
req.Header.Add("Content-Type", " application/json")
req.Header.Add("Accept", " application/json")
req.Header.Add("Authorization", "Bearer "+accessToken)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
postResultBody := string(body)
err = json.Unmarshal([]byte(postResultBody), &resultResponse)
if err != nil {
return err
}
return nil
}
func retrieveReleases(accessToken string, allReleases map[string]interface{}) error {
req, err := http.NewRequest("GET", pivnetApiUrl+"products/"+pivnetProduct+"/releases", nil)
if err != nil {
return err
}
req.Header.Add("Authorization", "Bearer "+accessToken)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
allReleasesAsString := string(body)
if err := json.Unmarshal([]byte(allReleasesAsString), &allReleases); err != nil {
return err
}
return nil
}
func readConf(filename string) (*PivnetConf, error) {
buf, err := os.ReadFile(filename)
if err != nil {
log.Fatal("Impossible to read "+filename, err)
return nil, err
}
pivnetConfig := &PivnetConf{}
err = yaml.Unmarshal(buf, pivnetConfig)
if err != nil {
return nil, fmt.Errorf("error loading Yaml file %q: %w", filename, err)
}
return pivnetConfig, err
}
A simple go app to connect to TanzuNet / Pivnet and update the release versions; to match the expected schema X.Y.Z instead of X.Y
@anthonydahanne
Copy link
Author

Example output:

✅ Successfully loaded pivnet local configuration at "/Users/anthonyd2/.pivnetrc"
✅ Successfully loaded pivnet access_token
✅ Successfully retrieved 88 releases for the TanzuNet product named java-buildpack
❓  OK to patch java-buildpack version 4.59.0 (release id 1307612) into version 4.59.0.0 ? y/n
n
❓  OK to patch java-buildpack version 4.58.0 (release id 1287490) into version 4.58.0.0 ? y/n
n
❓  OK to patch java-buildpack version 4.57 (release id 1283688) into version 4.57.0 ? y/n
y
2023/07/06 23:58:29 successfully updated release '1283688' for product 'java-buildpack'
❓  OK to patch java-buildpack version 4.56 (release id 1270364) into version 4.56.0 ? y/n

[...]

@pivotal-david-osullivan
Copy link

pivotal-david-osullivan commented Jul 7, 2023

Nice work! 🎉

I made a small addition to skip the already 3-digit versions so it doesn't ask us about those:

func main() {
pattern := regexp.MustCompile("([\\d]+.[\\d]+.[\\d]+)")

and then later, in the loop

version := currentRelease["version"].(string)
if pattern.MatchString(version) {
	continue
}

it seems to work, skipping to 4.56 for the first question

@anthonydahanne
Copy link
Author

@pivotal-david-osullivan thank you! I applied your suggestion and then added a log line to show we skipped

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment