<commit_before>/*
cperepos analyzes the NVD CPE Dictionary for Open Source repository information.
It reads the NVD CPE Dictionary XML file and outputs a JSON map of CPE products to discovered repository URLs.

It can also output on stdout additional data about colliding CPE package names.

Usage:

	go run cmd/cperepos/main.go [flags]

The flags are:

	  --cpe_dictionary
		The path to the uncompressed NVD CPE Dictionary XML file, see https://nvd.nist.gov/products/cpe

	  --output_dir
	        The directory to output cpe_product_to_repo.json and cpe_reference_description_frequency.csv in

	  --gcp_logging_project
		The GCP project ID to utilise for Cloud Logging. Set to the empty string to log to stdout

	  --verbose
		Output additional telemetry to stdout
*/
package main

import (
	"context"
	"encoding/csv"
	"encoding/json"
	"encoding/xml"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"

	"cloud.google.com/go/logging"
	"github.com/google/osv/vulnfeeds/cves"
	"github.com/google/osv/vulnfeeds/utility"
	"golang.org/x/exp/slices"
)

type CPEDict struct {
	XMLName  xml.Name  `xml:"cpe-list"`
	CPEItems []CPEItem `xml:"cpe-item"`
}

type CPEItem struct {
	XMLName    xml.Name    `xml:"cpe-item" json:"-"`
	Name       string      `xml:"name,attr" json:"name"`
	Title      string      `xml:"title" json:"title"`
	References []Reference `xml:"references>reference" json:"references"`
	CPE23      CPE23Item   `xml:"cpe23-item" json:"cpe23-item"`
}

type Reference struct {
	URL         string `xml:"href,attr" json:"URL"`
	Description string `xml:",chardata" json:"description"`
}

type CPE23Item struct {
	Name string `xml:"name,attr"`
}

const (
	CPEDictionaryDefault = "cve_jsons/nvdcpematch-1.0.json"
	OutputDirDefault     = "."
	projectId            = "oss-vdb"
)

var (
	Logger utility.LoggerWrapper
	// These repos should never be considered authoritative for a product.
	InvalidRepos = []string{
		"https://github.com/CVEProject/cvelist", // Heavily in Advisory URLs, sometimes shows up elsewhere
		"https://github.com/github/cvelist",     // Fork of the above
	}
	CPEDictionaryFile = flag.String("cpe_dictionary", CPEDictionaryDefault, "CPE Dictionary file to parse")
	OutputDir         = flag.String("output_dir", OutputDirDefault, "Directory to output cpe_product_to_repo.json and cpe_reference_description_frequency.csv in")
	GCPLoggingProject = flag.String("gcp_logging_project", projectId, "GCP project ID to use for logging, set to an empty string to log locally only")
	Verbose           = flag.Bool("verbose", false, "Output some telemetry to stdout during execution")
)

func LoadCPEDictionary(f string) (CPEDict, error) {
	xmlFile, err := os.Open(f)
	if err != nil {
		Logger.Fatalf("Failed to open %s: %v", f, err)
	}

	defer xmlFile.Close()

	byteValue, _ := ioutil.ReadAll(xmlFile)

	var c CPEDict
	xml.Unmarshal(byteValue, &c)

	return c, nil
}

// Outputs a JSON file of the product-to-repo map.
func outputProductToRepoMap(prm map[string]*string, f io.Writer) error {
	productsWithoutRepos := 0
	for p := range prm {
		if prm[p] == nil {
			productsWithoutRepos++
			delete(prm, p) // we don't want the repo-less products in our JSON output
			continue
		}
	}

	e := json.NewEncoder(f)

	if err := e.Encode(&prm); err != nil {
		return err
	}

	if *Verbose {
		fmt.Printf("Loaded information about %d products, %d do not have repos\n", len(prm), productsWithoutRepos)
	}
	Logger.Infof("Loaded information about %d application products, %d do not have repos", len(prm), productsWithoutRepos)
	return nil
}

// Outputs a CSV file of the description frequency map, sorted in descending order.
func outputDescriptionFrequency(df map[string]int, f io.Writer) error {
	descriptions := make([]string, 0, len(df))
	for description := range df {
		descriptions = append(descriptions, description)
	}
	sort.SliceStable(descriptions, func(i, j int) bool {
		return df[descriptions[i]] > df[descriptions[j]]
	})

	w := csv.NewWriter(f)

	if err := w.Write([]string{"Description", "Frequency"}); err != nil {
		return err
	}
	for _, d := range descriptions {
		if err := w.Write([]string{d, strconv.Itoa(df[d])}); err != nil {
			return err
		}
	}

	w.Flush()

	if err := w.Error(); err != nil {
		return err
	}

	if *Verbose {
		fmt.Printf("Seen %d descriptions \n", len(df))
	}
	Logger.Infof("Seen %d descriptions", len(df))
	return nil
}

// Analyze CPE Dictionary
func analyzeCPEDictionary(d CPEDict) (ProductToRepo map[string]*string, DescriptionFrequency map[string]int, ProductToVendor map[string][]string) {
	ProductToRepo = make(map[string]*string)
	DescriptionFrequency = make(map[string]int)
	ProductToVendor = make(map[string][]string)
	for _, c := range d.CPEItems {
		CPE, err := cves.ParseCPE(c.CPE23.Name)
		if err != nil {
			Logger.Infof("Failed to parse %q", c.CPE23.Name)
			continue
		}
		if CPE.Part != "a" {
			// Not interested in hardware or operating systems.
			continue
		}
		// Gather the data to answer the question: "are there product name collisions?"
		if *Verbose {
			if _, exists := ProductToVendor[CPE.Product]; exists {
				if !slices.Contains(ProductToVendor[CPE.Product], CPE.Vendor) {
					ProductToVendor[CPE.Product] = append(ProductToVendor[CPE.Product], CPE.Vendor)
				}
			} else {
				ProductToVendor[CPE.Product] = []string{CPE.Vendor}
			}
		}
		if _, exists := ProductToRepo[CPE.Product]; !exists {
			// This way every seen product will exist in the map,
			// and ones we never determine a repo for will have a
			// nil value.
			ProductToRepo[CPE.Product] = nil
		} else {
			if ProductToRepo[CPE.Product] != nil {
				Logger.Infof("Already have %q for %q, skipping relookup", *ProductToRepo[CPE.Product], CPE.Product)
				continue
			}
		}
		for _, r := range c.References {
			DescriptionFrequency[r.Description] += 1
			repo, err := cves.Repo(r.URL)
			if err != nil {
				Logger.Infof("Disregarding %q for %q (%s) because %v", r.URL, CPE.Product, r.Description, err)
				continue
			}
			// Disregard the repos we know we don't like.
			if slices.Contains(InvalidRepos, repo) {
				Logger.Infof("Disliking %q for %q (%s)", repo, CPE.Product, r.Description)
				continue
			}
			Logger.Infof("Liking %q for %q (%s)", repo, CPE.Product, r.Description)
			ProductToRepo[CPE.Product] = &repo
		}
	}
	return ProductToRepo, DescriptionFrequency, ProductToVendor
}

func main() {
	flag.Usage = func() {
		fmt.Fprint(flag.CommandLine.Output(), "Utility to analyze NVD CPE Dictionary\n\n")
		fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s:\n", os.Args[0])
		flag.PrintDefaults()
	}
	flag.Parse()

	if *GCPLoggingProject != "" {
		client, err := logging.NewClient(context.Background(), *GCPLoggingProject)
		if err != nil {
			log.Fatalf("Failed to create client: %v", err)
		}
		defer client.Close()
		Logger.GCloudLogger = client.Logger("cperepos")
	}

	CPEDictionary, err := LoadCPEDictionary(*CPEDictionaryFile)
	if err != nil {
		Logger.Fatalf("Failed to load %s: %v", *CPEDictionaryFile, err)
	}

	productToRepo, descriptionFrequency, productToVendor := analyzeCPEDictionary(CPEDictionary)

	mappingFile, err := os.Create(filepath.Join(*OutputDir, "cpe_product_to_repo.json"))
	if err != nil {
		Logger.Fatalf("%v", err)
	}
	defer mappingFile.Close()
	err = outputProductToRepoMap(productToRepo, mappingFile)
	if err != nil {
		Logger.Fatalf("%v", err)
	}
	// Answer the question: "are there product name collisions?"
	if *Verbose {
		for product, vendors := range productToVendor {
			if len(vendors) == 1 {
				continue
			}
			Logger.Infof("Product %s has >1 vendors: [%s]", product, strings.Join(vendors, ", "))
			fmt.Printf("Product %s has >1 vendors: [%s]\n", product, strings.Join(vendors, ", "))
		}
	}
	frequencyFile, err := os.Create(filepath.Join(*OutputDir, "cpe_reference_description_frequency.csv"))
	if err != nil {
		Logger.Fatalf("%v", err)
	}
	defer frequencyFile.Close()
	err = outputDescriptionFrequency(descriptionFrequency, frequencyFile)
	if err != nil {
		Logger.Fatalf("%v", err)
	}
}
<commit_msg>Improve invalid repo exclusion based on eyeballing the results (#854)<commit_after>/*
cperepos analyzes the NVD CPE Dictionary for Open Source repository information.
It reads the NVD CPE Dictionary XML file and outputs a JSON map of CPE products to discovered repository URLs.

It can also output on stdout additional data about colliding CPE package names.

Usage:

	go run cmd/cperepos/main.go [flags]

The flags are:

	  --cpe_dictionary
		The path to the uncompressed NVD CPE Dictionary XML file, see https://nvd.nist.gov/products/cpe

	  --output_dir
	        The directory to output cpe_product_to_repo.json and cpe_reference_description_frequency.csv in

	  --gcp_logging_project
		The GCP project ID to utilise for Cloud Logging. Set to the empty string to log to stdout

	  --verbose
		Output additional telemetry to stdout
*/
package main

import (
	"context"
	"encoding/csv"
	"encoding/json"
	"encoding/xml"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"

	"cloud.google.com/go/logging"
	"github.com/google/osv/vulnfeeds/cves"
	"github.com/google/osv/vulnfeeds/utility"
	"golang.org/x/exp/slices"
)

type CPEDict struct {
	XMLName  xml.Name  `xml:"cpe-list"`
	CPEItems []CPEItem `xml:"cpe-item"`
}

type CPEItem struct {
	XMLName    xml.Name    `xml:"cpe-item" json:"-"`
	Name       string      `xml:"name,attr" json:"name"`
	Title      string      `xml:"title" json:"title"`
	References []Reference `xml:"references>reference" json:"references"`
	CPE23      CPE23Item   `xml:"cpe23-item" json:"cpe23-item"`
}

type Reference struct {
	URL         string `xml:"href,attr" json:"URL"`
	Description string `xml:",chardata" json:"description"`
}

type CPE23Item struct {
	Name string `xml:"name,attr"`
}

const (
	CPEDictionaryDefault = "cve_jsons/nvdcpematch-1.0.json"
	OutputDirDefault     = "."
	projectId            = "oss-vdb"
)

var (
	Logger utility.LoggerWrapper
	// These repos should never be considered authoritative for a product.
	// TODO(apollock): read this from an external file
	InvalidRepos = []string{
		"https://github.com/abhiunix/goo-blog-App-CVE",
		"https://github.com/agadient/SERVEEZ-CVE",
		"https://github.com/alwentiu/COVIDSafe-CVE-2020-12856",
		"https://github.com/ciph0x01/Simple-Exam-Reviewer-Management-System-CVE",
		"https://github.com/CVEProject/cvelist", // Heavily in Advisory URLs, sometimes shows up elsewhere
		"https://github.com/DayiliWaseem/CVE-2022-39196-",
		"https://github.com/eddietcc/CVEnotes",
		"https://github.com/Fadavvi/CVE-2018-17431-PoC",
		"https://github.com/GitHubAssessments/CVE_Assessments_11_2019",
		"https://github.com/github/cvelist", // Fork of https://github.com/CVEProject/cvelist
		"https://github.com/Gr4y21/My-CVE-IDs",
		"https://github.com/hemantsolo/CVE-Reference",
		"https://github.com/huclilu/CVE_Add",
		"https://github.com/i3umi3iei3ii/CentOS-Control-Web-Panel-CVE",
		"https://github.com/Kenun99/CVE-batdappboomx",
		"https://github.com/lukaszstu/SmartAsset-CORS-CVE-2020-26527",
		"https://github.com/MacherCS/CVE_Evoh_Contract",
		"https://github.com/martinkubecka/CVE-References",
		"https://github.com/MrR3boot/CVE-Hunting",
		"https://github.com/nu11secur1ty/CVE-nu11secur1ty",
		"https://github.com/Orange-Cyberdefense/CVE-repository",
		"https://github.com/post-cyberlabs/CVE-Advisory",
		"https://github.com/refi64/CVE-2020-25265-25266",
		"https://github.com/riteshgohil/My_CVE_References",
		"https://github.com/roughb8722/CVE-2021-3122-Details",
		"https://github.com/Ryan0lb/EC-cloud-e-commerce-system-CVE-application",
		"https://github.com/SaumyajeetDas/POC-of-CVE-2022-36271",
		"https://github.com/Security-AVS/-CVE-2021-26904",
		"https://github.com/vQAQv/Request-CVE-ID-PoC",
		"https://github.com/wsummerhill/BSA-Radar_CVE-Vulnerabilities",
		"https://github.com/xiahao90/CVEproject",
		"https://github.com/z00z00z00/Safenet_SAC_CVE-2021-42056",
	}
	// Match repos with "CVE", "CVEs" or a pure CVE number in their name, anything from GitHubAssessments
	InvalidRepoRegex  = `/(?:(?:CVEs?)|(?:CVE-\d{4}-\d{4,})|GitHubAssessments/.*)`
	CPEDictionaryFile = flag.String("cpe_dictionary", CPEDictionaryDefault, "CPE Dictionary file to parse")
	OutputDir         = flag.String("output_dir", OutputDirDefault, "Directory to output cpe_product_to_repo.json and cpe_reference_description_frequency.csv in")
	GCPLoggingProject = flag.String("gcp_logging_project", projectId, "GCP project ID to use for logging, set to an empty string to log locally only")
	Verbose           = flag.Bool("verbose", false, "Output some telemetry to stdout during execution")
)

func LoadCPEDictionary(f string) (CPEDict, error) {
	xmlFile, err := os.Open(f)
	if err != nil {
		Logger.Fatalf("Failed to open %s: %v", f, err)
	}

	defer xmlFile.Close()

	byteValue, _ := ioutil.ReadAll(xmlFile)

	var c CPEDict
	xml.Unmarshal(byteValue, &c)

	return c, nil
}

// Outputs a JSON file of the product-to-repo map.
func outputProductToRepoMap(prm map[string]*string, f io.Writer) error {
	productsWithoutRepos := 0
	for p := range prm {
		if prm[p] == nil {
			productsWithoutRepos++
			delete(prm, p) // we don't want the repo-less products in our JSON output
			continue
		}
	}

	e := json.NewEncoder(f)

	if err := e.Encode(&prm); err != nil {
		return err
	}

	if *Verbose {
		fmt.Printf("Loaded information about %d products, %d do not have repos\n", len(prm), productsWithoutRepos)
	}
	Logger.Infof("Loaded information about %d application products, %d do not have repos", len(prm), productsWithoutRepos)
	return nil
}

// Outputs a CSV file of the description frequency map, sorted in descending order.
func outputDescriptionFrequency(df map[string]int, f io.Writer) error {
	descriptions := make([]string, 0, len(df))
	for description := range df {
		descriptions = append(descriptions, description)
	}
	sort.SliceStable(descriptions, func(i, j int) bool {
		return df[descriptions[i]] > df[descriptions[j]]
	})

	w := csv.NewWriter(f)

	if err := w.Write([]string{"Description", "Frequency"}); err != nil {
		return err
	}
	for _, d := range descriptions {
		if err := w.Write([]string{d, strconv.Itoa(df[d])}); err != nil {
			return err
		}
	}

	w.Flush()

	if err := w.Error(); err != nil {
		return err
	}

	if *Verbose {
		fmt.Printf("Seen %d descriptions \n", len(df))
	}
	Logger.Infof("Seen %d descriptions", len(df))
	return nil
}

// Analyze CPE Dictionary
func analyzeCPEDictionary(d CPEDict) (ProductToRepo map[string]*string, DescriptionFrequency map[string]int, ProductToVendor map[string][]string) {
	ProductToRepo = make(map[string]*string)
	DescriptionFrequency = make(map[string]int)
	ProductToVendor = make(map[string][]string)
	for _, c := range d.CPEItems {
		CPE, err := cves.ParseCPE(c.CPE23.Name)
		if err != nil {
			Logger.Infof("Failed to parse %q", c.CPE23.Name)
			continue
		}
		if CPE.Part != "a" {
			// Not interested in hardware or operating systems.
			continue
		}
		// Gather the data to answer the question: "are there product name collisions?"
		if *Verbose {
			if _, exists := ProductToVendor[CPE.Product]; exists {
				if !slices.Contains(ProductToVendor[CPE.Product], CPE.Vendor) {
					ProductToVendor[CPE.Product] = append(ProductToVendor[CPE.Product], CPE.Vendor)
				}
			} else {
				ProductToVendor[CPE.Product] = []string{CPE.Vendor}
			}
		}
		if _, exists := ProductToRepo[CPE.Product]; !exists {
			// This way every seen product will exist in the map,
			// and ones we never determine a repo for will have a
			// nil value.
			ProductToRepo[CPE.Product] = nil
		} else {
			if ProductToRepo[CPE.Product] != nil {
				Logger.Infof("Already have %q for %q, skipping relookup", *ProductToRepo[CPE.Product], CPE.Product)
				continue
			}
		}
		for _, r := range c.References {
			DescriptionFrequency[r.Description] += 1
			repo, err := cves.Repo(r.URL)
			if err != nil {
				Logger.Infof("Disregarding %q for %q (%s) because %v", r.URL, CPE.Product, r.Description, err)
				continue
			}
			// Disregard the repos we know we don't like.
			matched, _ := regexp.MatchString(InvalidRepoRegex, repo)
			if matched {
				Logger.Infof("Disliking %q for %q (%s) (matched regexp)", repo, CPE.Product, r.Description)
				continue
			}
			if slices.Contains(InvalidRepos, repo) {
				Logger.Infof("Disliking %q for %q (%s)", repo, CPE.Product, r.Description)
				continue
			}
			Logger.Infof("Liking %q for %q (%s)", repo, CPE.Product, r.Description)
			ProductToRepo[CPE.Product] = &repo
		}
	}
	return ProductToRepo, DescriptionFrequency, ProductToVendor
}

func main() {
	flag.Usage = func() {
		fmt.Fprint(flag.CommandLine.Output(), "Utility to analyze NVD CPE Dictionary\n\n")
		fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s:\n", os.Args[0])
		flag.PrintDefaults()
	}
	flag.Parse()

	if *GCPLoggingProject != "" {
		client, err := logging.NewClient(context.Background(), *GCPLoggingProject)
		if err != nil {
			log.Fatalf("Failed to create client: %v", err)
		}
		defer client.Close()
		Logger.GCloudLogger = client.Logger("cperepos")
	}

	CPEDictionary, err := LoadCPEDictionary(*CPEDictionaryFile)
	if err != nil {
		Logger.Fatalf("Failed to load %s: %v", *CPEDictionaryFile, err)
	}

	productToRepo, descriptionFrequency, productToVendor := analyzeCPEDictionary(CPEDictionary)

	mappingFile, err := os.Create(filepath.Join(*OutputDir, "cpe_product_to_repo.json"))
	if err != nil {
		Logger.Fatalf("%v", err)
	}
	defer mappingFile.Close()
	err = outputProductToRepoMap(productToRepo, mappingFile)
	if err != nil {
		Logger.Fatalf("%v", err)
	}
	// Answer the question: "are there product name collisions?"
	if *Verbose {
		for product, vendors := range productToVendor {
			if len(vendors) == 1 {
				continue
			}
			Logger.Infof("Product %s has >1 vendors: [%s]", product, strings.Join(vendors, ", "))
			fmt.Printf("Product %s has >1 vendors: [%s]\n", product, strings.Join(vendors, ", "))
		}
	}
	frequencyFile, err := os.Create(filepath.Join(*OutputDir, "cpe_reference_description_frequency.csv"))
	if err != nil {
		Logger.Fatalf("%v", err)
	}
	defer frequencyFile.Close()
	err = outputDescriptionFrequency(descriptionFrequency, frequencyFile)
	if err != nil {
		Logger.Fatalf("%v", err)
	}
}
<|endoftext|>


<commit_before>package runkeeper

import (
	"bytes"
	"encoding/json"
	"fmt"
	latlong "github.com/svdberg/syncmysport-runkeeper/Godeps/_workspace/src/github.com/bradfitz/latlong"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strconv"
	"strings"
	"time"
)

const (
	ContentTypeBackgroundActivity       = "application/vnd.com.runkeeper.BackgroundActivity+json"
	ContentTypeBackgroundActivitySet    = "application/vnd.com.runkeeper.BackgroundActivitySet+json"
	ContentTypeComment                  = "application/vnd.com.runkeeper.Comment+json"
	ContentTypeDiabetesMeasurementSet   = "application/vnd.com.runkeeper.DiabetesMeasurementSet+json"
	ContentTypeFitnessActivity          = "application/vnd.com.runkeeper.FitnessActivity+json"
	ContentTypeFitnessActivityFeed      = "application/vnd.com.runkeeper.FitnessActivityFeed+json"
	ContentTypeGeneralMeasurementSet    = "application/vnd.com.runkeeper.GeneralMeasurementSet+json"
	ContentTypeNutritionSet             = "application/vnd.com.runkeeper.NutritionSet+json"
	ContentTypeProfile                  = "application/vnd.com.runkeeper.Profile+json"
	ContentTypeSleepSet                 = "application/vnd.com.runkeeper.SleepSet+json"
	ContentTypeStrengthTrainingActivity = "application/vnd.com.runkeeper.StrengthTrainingActivity+json"
	ContentTypeUser                     = "application/vnd.com.runkeeper.User+json"
	ContentTypeWeightSet                = "application/vnd.com.runkeeper.WeightSet+json"
)
const (
	ContentTypeNewSleep    = "application/vnd.com.runkeeper.NewSleep+json"
	ContentTypeNewActivity = "application/vnd.com.runkeeper.NewFitnessActivity+json"
)

const baseUrl = "https://api.runkeeper.com"
const deAuthUrl = "https://runkeeper.com/apps/de-authorize"

type Client struct {
	AccessToken string
	CookieJar   *cookiejar.Jar
	*http.Client
}

func NewClient(accessToken string) *Client {
	jar, err := cookiejar.New(nil)
	if err != nil {
		panic(err)
	}
	return &Client{accessToken, jar, &http.Client{Jar: jar}}
}

func (self *Client) Deauthorize() error {
	//https://runkeeper.com/apps/de-authorize
	tokenString := fmt.Sprintf("{\"access_token\":\"%s\"}", self.AccessToken)
	body := strings.NewReader(tokenString)
	req, err := http.NewRequest("POST", deAuthUrl, body)
	if err != nil {
		return err
	}
	_, err = self.Do(req)
	if err != nil {
		return err
	}
	return nil
}

/**
result should be a struct pointer
*/
func parseJsonResponse(resp *http.Response, result interface{}) error {
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	return json.Unmarshal(body, result)
}

func (self *Client) createBaseRequest(method string, url string, contentType string, body io.Reader) (*http.Request, error) {
	req, err := http.NewRequest(method, baseUrl+url, body)
	if method != "POST" {
		req.Header.Add("Accept", contentType)
	} else {
		req.Header.Add("Content-Type", contentType)
	}
	req.Header.Add("Authorization", "Bearer "+self.AccessToken)
	if err != nil {
		return nil, err
	}
	return req, nil
}

type Params map[string]interface{}

func (self *Client) GetRequestParams(userParams *Params) url.Values {
	params := url.Values{}
	if userParams != nil {
		for key, val := range *userParams {
			switch t := val.(type) {
			case int, int8, int16, int32, int64:
				params.Set(key, strconv.Itoa(t.(int)))
			case string:
				params.Set(key, t)
			case []byte:
				params.Set(key, string(t))
			default:
				params.Set(key, t.(string))
			}
		}
	}
	return params
}

func (self *Client) GetUser() (*User, error) {
	req, err := self.createBaseRequest("GET", "/User", ContentTypeUser, nil)
	if err != nil {
		return nil, err
	}
	resp, err := self.Do(req)
	if err != nil {
		return nil, err
	}
	var user = User{}
	defer resp.Body.Close()
	if err := parseJsonResponse(resp, &user); err != nil {
		return nil, err
	}
	return &user, nil
}

/**
Valid params:
	page: (int)
	pageSize: (int)
*/
func (self *Client) GetFitnessActivityFeed(userParams *Params) (*FitnessActivityFeed, error) {
	params := self.GetRequestParams(userParams)
	req, err := self.createBaseRequest("GET", "/fitnessActivities?"+params.Encode(), ContentTypeFitnessActivityFeed, nil)
	if err != nil {
		return nil, err
	}

	resp, err := self.Do(req)
	if err != nil {
		return nil, err
	}
	var activities = FitnessActivityFeed{}
	defer resp.Body.Close()
	if err := parseJsonResponse(resp, &activities); err != nil {
		return nil, err
	}
	return &activities, nil
}

func (self *Client) GetFitnessActivity(activityUri string, userParams *Params) (*FitnessActivity, error) {
	params := self.GetRequestParams(userParams)
	req, err := self.createBaseRequest("GET", activityUri+"?"+params.Encode(), ContentTypeFitnessActivity, nil)
	if err != nil {
		return nil, err
	}

	resp, err := self.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var activity = FitnessActivity{}

	// re-fill the details
	if err := parseJsonResponse(resp, &activity); err != nil {
		return nil, err
	}

	//Now that we know the activity, Calculate the TZ the activity was in, and change the StartTime to UTC,
	//and store the UTC offset.
	activitiesLocation := calculateLocationOfActivity(&activity)
	writeTimeOffsetFromUTC(&activity, activitiesLocation)
	correctTimeForOffsetFromUTC(&activity)
	return &activity, nil
}

func correctTimeForOffsetFromUTC(activity *FitnessActivity) {
	correctedTime := Time(time.Time(activity.StartTime).Add(time.Duration(-1*activity.UtcOffset) * time.Hour))
	activity.StartTime = correctedTime
}

func writeTimeOffsetFromUTC(activity *FitnessActivity, location *time.Location) {
	//in case of UTC we dont do anything, we assume the time is already in UTC
	if location != time.UTC {
		timeInTZ := time.Time(activity.StartTime).In(location)
		_, offsetInSeconds := timeInTZ.Zone()
		activity.UtcOffset = offsetInSeconds / 60 / 60
	} else {
		activity.UtcOffset = 0
	}
}

func calculateLocationOfActivity(activity *FitnessActivity) *time.Location {
	//get the first lat/long from the GPS track
	if len(activity.Path) > 0 {
		latLong := activity.Path[0]
		timeZone := latlong.LookupZoneName(latLong.Latitude, latLong.Longitude)
		location, err := time.LoadLocation(timeZone)
		if err != nil {
			return time.UTC
		}
		return location
	} else {
		//Assume UTC??
		return time.UTC
	}
	//cant happen
	return nil
}

func (self *Client) PostNewFitnessActivity(activity *FitnessActivityNew) (string, error) {
	payload, err := json.Marshal(activity)
	req, err := self.createBaseRequest("POST", "/fitnessActivities", ContentTypeNewActivity, bytes.NewBuffer(payload))
	if err != nil {
		return "", err
	}

	resp, err := self.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.Status == "201 Created" {
		return resp.Header.Get("Location"), nil
	}

	return "", fmt.Errorf("Activity not created, no 201 returned, got %s", resp.Status)

}
<commit_msg>User != user<commit_after>package runkeeper

import (
	"bytes"
	"encoding/json"
	"fmt"
	latlong "github.com/svdberg/syncmysport-runkeeper/Godeps/_workspace/src/github.com/bradfitz/latlong"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strconv"
	"strings"
	"time"
)

const (
	ContentTypeBackgroundActivity       = "application/vnd.com.runkeeper.BackgroundActivity+json"
	ContentTypeBackgroundActivitySet    = "application/vnd.com.runkeeper.BackgroundActivitySet+json"
	ContentTypeComment                  = "application/vnd.com.runkeeper.Comment+json"
	ContentTypeDiabetesMeasurementSet   = "application/vnd.com.runkeeper.DiabetesMeasurementSet+json"
	ContentTypeFitnessActivity          = "application/vnd.com.runkeeper.FitnessActivity+json"
	ContentTypeFitnessActivityFeed      = "application/vnd.com.runkeeper.FitnessActivityFeed+json"
	ContentTypeGeneralMeasurementSet    = "application/vnd.com.runkeeper.GeneralMeasurementSet+json"
	ContentTypeNutritionSet             = "application/vnd.com.runkeeper.NutritionSet+json"
	ContentTypeProfile                  = "application/vnd.com.runkeeper.Profile+json"
	ContentTypeSleepSet                 = "application/vnd.com.runkeeper.SleepSet+json"
	ContentTypeStrengthTrainingActivity = "application/vnd.com.runkeeper.StrengthTrainingActivity+json"
	ContentTypeUser                     = "application/vnd.com.runkeeper.User+json"
	ContentTypeWeightSet                = "application/vnd.com.runkeeper.WeightSet+json"
)
const (
	ContentTypeNewSleep    = "application/vnd.com.runkeeper.NewSleep+json"
	ContentTypeNewActivity = "application/vnd.com.runkeeper.NewFitnessActivity+json"
)

const baseUrl = "https://api.runkeeper.com"
const deAuthUrl = "https://runkeeper.com/apps/de-authorize"

type Client struct {
	AccessToken string
	CookieJar   *cookiejar.Jar
	*http.Client
}

func NewClient(accessToken string) *Client {
	jar, err := cookiejar.New(nil)
	if err != nil {
		panic(err)
	}
	return &Client{accessToken, jar, &http.Client{Jar: jar}}
}

func (self *Client) Deauthorize() error {
	//https://runkeeper.com/apps/de-authorize
	tokenString := fmt.Sprintf("{\"access_token\":\"%s\"}", self.AccessToken)
	body := strings.NewReader(tokenString)
	req, err := http.NewRequest("POST", deAuthUrl, body)
	if err != nil {
		return err
	}
	_, err = self.Do(req)
	if err != nil {
		return err
	}
	return nil
}

/**
result should be a struct pointer
*/
func parseJsonResponse(resp *http.Response, result interface{}) error {
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	return json.Unmarshal(body, result)
}

func (self *Client) createBaseRequest(method string, url string, contentType string, body io.Reader) (*http.Request, error) {
	req, err := http.NewRequest(method, baseUrl+url, body)
	if method != "POST" {
		req.Header.Add("Accept", contentType)
	} else {
		req.Header.Add("Content-Type", contentType)
	}
	req.Header.Add("Authorization", "Bearer "+self.AccessToken)
	if err != nil {
		return nil, err
	}
	return req, nil
}

type Params map[string]interface{}

func (self *Client) GetRequestParams(userParams *Params) url.Values {
	params := url.Values{}
	if userParams != nil {
		for key, val := range *userParams {
			switch t := val.(type) {
			case int, int8, int16, int32, int64:
				params.Set(key, strconv.Itoa(t.(int)))
			case string:
				params.Set(key, t)
			case []byte:
				params.Set(key, string(t))
			default:
				params.Set(key, t.(string))
			}
		}
	}
	return params
}

func (self *Client) GetUser() (*User, error) {
	req, err := self.createBaseRequest("GET", "/user", ContentTypeUser, nil)
	if err != nil {
		return nil, err
	}
	resp, err := self.Do(req)
	if err != nil {
		return nil, err
	}
	var user = User{}
	defer resp.Body.Close()
	if err := parseJsonResponse(resp, &user); err != nil {
		return nil, err
	}
	return &user, nil
}

/**
Valid params:
	page: (int)
	pageSize: (int)
*/
func (self *Client) GetFitnessActivityFeed(userParams *Params) (*FitnessActivityFeed, error) {
	params := self.GetRequestParams(userParams)
	req, err := self.createBaseRequest("GET", "/fitnessActivities?"+params.Encode(), ContentTypeFitnessActivityFeed, nil)
	if err != nil {
		return nil, err
	}

	resp, err := self.Do(req)
	if err != nil {
		return nil, err
	}
	var activities = FitnessActivityFeed{}
	defer resp.Body.Close()
	if err := parseJsonResponse(resp, &activities); err != nil {
		return nil, err
	}
	return &activities, nil
}

func (self *Client) GetFitnessActivity(activityUri string, userParams *Params) (*FitnessActivity, error) {
	params := self.GetRequestParams(userParams)
	req, err := self.createBaseRequest("GET", activityUri+"?"+params.Encode(), ContentTypeFitnessActivity, nil)
	if err != nil {
		return nil, err
	}

	resp, err := self.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var activity = FitnessActivity{}

	// re-fill the details
	if err := parseJsonResponse(resp, &activity); err != nil {
		return nil, err
	}

	//Now that we know the activity, Calculate the TZ the activity was in, and change the StartTime to UTC,
	//and store the UTC offset.
	activitiesLocation := calculateLocationOfActivity(&activity)
	writeTimeOffsetFromUTC(&activity, activitiesLocation)
	correctTimeForOffsetFromUTC(&activity)
	return &activity, nil
}

func correctTimeForOffsetFromUTC(activity *FitnessActivity) {
	correctedTime := Time(time.Time(activity.StartTime).Add(time.Duration(-1*activity.UtcOffset) * time.Hour))
	activity.StartTime = correctedTime
}

func writeTimeOffsetFromUTC(activity *FitnessActivity, location *time.Location) {
	//in case of UTC we dont do anything, we assume the time is already in UTC
	if location != time.UTC {
		timeInTZ := time.Time(activity.StartTime).In(location)
		_, offsetInSeconds := timeInTZ.Zone()
		activity.UtcOffset = offsetInSeconds / 60 / 60
	} else {
		activity.UtcOffset = 0
	}
}

func calculateLocationOfActivity(activity *FitnessActivity) *time.Location {
	//get the first lat/long from the GPS track
	if len(activity.Path) > 0 {
		latLong := activity.Path[0]
		timeZone := latlong.LookupZoneName(latLong.Latitude, latLong.Longitude)
		location, err := time.LoadLocation(timeZone)
		if err != nil {
			return time.UTC
		}
		return location
	} else {
		//Assume UTC??
		return time.UTC
	}
	//cant happen
	return nil
}

func (self *Client) PostNewFitnessActivity(activity *FitnessActivityNew) (string, error) {
	payload, err := json.Marshal(activity)
	req, err := self.createBaseRequest("POST", "/fitnessActivities", ContentTypeNewActivity, bytes.NewBuffer(payload))
	if err != nil {
		return "", err
	}

	resp, err := self.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.Status == "201 Created" {
		return resp.Header.Get("Location"), nil
	}

	return "", fmt.Errorf("Activity not created, no 201 returned, got %s", resp.Status)

}
<|endoftext|>


<commit_before>// Copyright 2016 Circonus, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Annotation API support - Fetch, Create, Update, Delete, and Search
// See: https://login.circonus.com/resources/api/calls/annotation

package api

import (
	"encoding/json"
	"fmt"
	"net/url"
	"regexp"

	"github.com/circonus-labs/circonus-gometrics/api/config"
)

// Annotation defines a annotation
type Annotation struct {
	CID            string   `json:"_cid,omitempty"`
	Created        uint     `json:"_created,omitempty"`
	LastModified   uint     `json:"_last_modified,omitempty"`
	LastModifiedBy string   `json:"_last_modified_by,omitempty"`
	Category       string   `json:"category"`
	Description    string   `json:"description"`
	RelatedMetrics []string `json:"rel_metrics"`
	Start          uint     `json:"start"`
	Stop           uint     `json:"stop"`
	Title          string   `json:"title"`
}

// FetchAnnotation retrieves a annotation definition
func (a *API) FetchAnnotation(cid CIDType) (*Annotation, error) {
	if cid == nil || *cid == "" {
		return nil, fmt.Errorf("Invalid annotation CID [none]")
	}

	annotationCID := string(*cid)

	matched, err := regexp.MatchString(config.AnnotationCIDRegex, annotationCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid annotation CID [%s]", annotationCID)
	}

	result, err := a.Get(annotationCID)
	if err != nil {
		return nil, err
	}

	annotation := &Annotation{}
	if err := json.Unmarshal(result, annotation); err != nil {
		return nil, err
	}

	return annotation, nil
}

// FetchAnnotations retrieves all annotations
func (a *API) FetchAnnotations() (*[]Annotation, error) {
	result, err := a.Get(config.AnnotationPrefix)
	if err != nil {
		return nil, err
	}

	var annotations []Annotation
	if err := json.Unmarshal(result, &annotations); err != nil {
		return nil, err
	}

	return &annotations, nil
}

// UpdateAnnotation update annotation definition
func (a *API) UpdateAnnotation(cfg *Annotation) (*Annotation, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid annotation config [nil]")
	}

	annotationCID := string(cfg.CID)

	matched, err := regexp.MatchString(config.AnnotationCIDRegex, annotationCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid annotation CID [%s]", annotationCID)
	}

	jsonCfg, err := json.Marshal(cfg)
	if err != nil {
		return nil, err
	}

	result, err := a.Put(annotationCID, jsonCfg)
	if err != nil {
		return nil, err
	}

	annotation := &Annotation{}
	if err := json.Unmarshal(result, annotation); err != nil {
		return nil, err
	}

	return annotation, nil
}

// CreateAnnotation create a new annotation
func (a *API) CreateAnnotation(cfg *Annotation) (*Annotation, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid annotation config [nil]")
	}

	jsonCfg, err := json.Marshal(cfg)
	if err != nil {
		return nil, err
	}

	result, err := a.Post(config.AnnotationPrefix, jsonCfg)
	if err != nil {
		return nil, err
	}

	annotation := &Annotation{}
	if err := json.Unmarshal(result, annotation); err != nil {
		return nil, err
	}

	return annotation, nil
}

// DeleteAnnotation delete a annotation
func (a *API) DeleteAnnotation(cfg *Annotation) (bool, error) {
	if cfg == nil {
		return false, fmt.Errorf("Invalid annotation config [none]")
	}

	return a.DeleteAnnotationByCID(CIDType(&cfg.CID))
}

// DeleteAnnotationByCID delete a annotation by cid
func (a *API) DeleteAnnotationByCID(cid CIDType) (bool, error) {
	if cid == nil || *cid == "" {
		return false, fmt.Errorf("Invalid annotation CID [none]")
	}

	annotationCID := string(*cid)

	matched, err := regexp.MatchString(config.AnnotationCIDRegex, annotationCID)
	if err != nil {
		return false, err
	}
	if !matched {
		return false, fmt.Errorf("Invalid annotation CID [%s]", annotationCID)
	}

	_, err = a.Delete(annotationCID)
	if err != nil {
		return false, err
	}

	return true, nil
}

// SearchAnnotations returns list of annotations matching a search query and/or filter
//    - a search query (see: https://login.circonus.com/resources/api#searching)
//    - a filter (see: https://login.circonus.com/resources/api#filtering)
func (a *API) SearchAnnotations(searchCriteria *SearchQueryType, filterCriteria *SearchFilterType) (*[]Annotation, error) {
	q := url.Values{}

	if searchCriteria != nil && *searchCriteria != "" {
		q.Set("search", string(*searchCriteria))
	}

	if filterCriteria != nil && len(*filterCriteria) > 0 {
		for filter, criteria := range *filterCriteria {
			for _, val := range criteria {
				q.Add(filter, val)
			}
		}
	}

	if q.Encode() == "" {
		return a.FetchAnnotations()
	}

	reqURL := url.URL{
		Path:     config.AnnotationPrefix,
		RawQuery: q.Encode(),
	}

	result, err := a.Get(reqURL.String())
	if err != nil {
		return nil, fmt.Errorf("[ERROR] API call error %+v", err)
	}

	var annotations []Annotation
	if err := json.Unmarshal(result, &annotations); err != nil {
		return nil, err
	}

	return &annotations, nil
}
<commit_msg>add: debug messages<commit_after>// Copyright 2016 Circonus, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Annotation API support - Fetch, Create, Update, Delete, and Search
// See: https://login.circonus.com/resources/api/calls/annotation

package api

import (
	"encoding/json"
	"fmt"
	"net/url"
	"regexp"

	"github.com/circonus-labs/circonus-gometrics/api/config"
)

// Annotation defines a annotation
type Annotation struct {
	CID            string   `json:"_cid,omitempty"`
	Created        uint     `json:"_created,omitempty"`
	LastModified   uint     `json:"_last_modified,omitempty"`
	LastModifiedBy string   `json:"_last_modified_by,omitempty"`
	Category       string   `json:"category"`
	Description    string   `json:"description"`
	RelatedMetrics []string `json:"rel_metrics"`
	Start          uint     `json:"start"`
	Stop           uint     `json:"stop"`
	Title          string   `json:"title"`
}

// FetchAnnotation retrieves a annotation definition
func (a *API) FetchAnnotation(cid CIDType) (*Annotation, error) {
	if cid == nil || *cid == "" {
		return nil, fmt.Errorf("Invalid annotation CID [none]")
	}

	annotationCID := string(*cid)

	matched, err := regexp.MatchString(config.AnnotationCIDRegex, annotationCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid annotation CID [%s]", annotationCID)
	}

	result, err := a.Get(annotationCID)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] fetch annotation, received JSON: %s", string(result))
	}

	annotation := &Annotation{}
	if err := json.Unmarshal(result, annotation); err != nil {
		return nil, err
	}

	return annotation, nil
}

// FetchAnnotations retrieves all annotations
func (a *API) FetchAnnotations() (*[]Annotation, error) {
	result, err := a.Get(config.AnnotationPrefix)
	if err != nil {
		return nil, err
	}

	var annotations []Annotation
	if err := json.Unmarshal(result, &annotations); err != nil {
		return nil, err
	}

	return &annotations, nil
}

// UpdateAnnotation update annotation definition
func (a *API) UpdateAnnotation(cfg *Annotation) (*Annotation, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid annotation config [nil]")
	}

	annotationCID := string(cfg.CID)

	matched, err := regexp.MatchString(config.AnnotationCIDRegex, annotationCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid annotation CID [%s]", annotationCID)
	}

	jsonCfg, err := json.Marshal(cfg)
	if err != nil {
		return nil, err
	}

	result, err := a.Put(annotationCID, jsonCfg)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] update annotation, sending JSON: %s", string(jsonCfg))
	}

	annotation := &Annotation{}
	if err := json.Unmarshal(result, annotation); err != nil {
		return nil, err
	}

	return annotation, nil
}

// CreateAnnotation create a new annotation
func (a *API) CreateAnnotation(cfg *Annotation) (*Annotation, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid annotation config [nil]")
	}

	jsonCfg, err := json.Marshal(cfg)
	if err != nil {
		return nil, err
	}

	result, err := a.Post(config.AnnotationPrefix, jsonCfg)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] create annotation, sending JSON: %s", string(jsonCfg))
	}

	annotation := &Annotation{}
	if err := json.Unmarshal(result, annotation); err != nil {
		return nil, err
	}

	return annotation, nil
}

// DeleteAnnotation delete a annotation
func (a *API) DeleteAnnotation(cfg *Annotation) (bool, error) {
	if cfg == nil {
		return false, fmt.Errorf("Invalid annotation config [none]")
	}

	return a.DeleteAnnotationByCID(CIDType(&cfg.CID))
}

// DeleteAnnotationByCID delete a annotation by cid
func (a *API) DeleteAnnotationByCID(cid CIDType) (bool, error) {
	if cid == nil || *cid == "" {
		return false, fmt.Errorf("Invalid annotation CID [none]")
	}

	annotationCID := string(*cid)

	matched, err := regexp.MatchString(config.AnnotationCIDRegex, annotationCID)
	if err != nil {
		return false, err
	}
	if !matched {
		return false, fmt.Errorf("Invalid annotation CID [%s]", annotationCID)
	}

	_, err = a.Delete(annotationCID)
	if err != nil {
		return false, err
	}

	return true, nil
}

// SearchAnnotations returns list of annotations matching a search query and/or filter
//    - a search query (see: https://login.circonus.com/resources/api#searching)
//    - a filter (see: https://login.circonus.com/resources/api#filtering)
func (a *API) SearchAnnotations(searchCriteria *SearchQueryType, filterCriteria *SearchFilterType) (*[]Annotation, error) {
	q := url.Values{}

	if searchCriteria != nil && *searchCriteria != "" {
		q.Set("search", string(*searchCriteria))
	}

	if filterCriteria != nil && len(*filterCriteria) > 0 {
		for filter, criteria := range *filterCriteria {
			for _, val := range criteria {
				q.Add(filter, val)
			}
		}
	}

	if q.Encode() == "" {
		return a.FetchAnnotations()
	}

	reqURL := url.URL{
		Path:     config.AnnotationPrefix,
		RawQuery: q.Encode(),
	}

	result, err := a.Get(reqURL.String())
	if err != nil {
		return nil, fmt.Errorf("[ERROR] API call error %+v", err)
	}

	var annotations []Annotation
	if err := json.Unmarshal(result, &annotations); err != nil {
		return nil, err
	}

	return &annotations, nil
}
<|endoftext|>


<commit_before>package dummy

import (
	"strings"

	. "github.com/tendermint/go-common"
	"github.com/tendermint/go-merkle"
	"github.com/tendermint/go-wire"
	"github.com/tendermint/tmsp/types"
)

type DummyApplication struct {
	state merkle.Tree
}

func NewDummyApplication() *DummyApplication {
	state := merkle.NewIAVLTree(0, nil)
	return &DummyApplication{state: state}
}

func (app *DummyApplication) Info() (string, *types.TMSPInfo, *types.LastBlockInfo, *types.ConfigInfo) {
	return Fmt("{\"size\":%v}", app.state.Size()), nil, nil, nil
}

func (app *DummyApplication) SetOption(key string, value string) (log string) {
	return ""
}

// tx is either "key=value" or just arbitrary bytes
func (app *DummyApplication) AppendTx(tx []byte) types.Result {
	parts := strings.Split(string(tx), "=")
	if len(parts) == 2 {
		app.state.Set([]byte(parts[0]), []byte(parts[1]))
	} else {
		app.state.Set(tx, tx)
	}
	return types.OK
}

func (app *DummyApplication) CheckTx(tx []byte) types.Result {
	return types.OK
}

func (app *DummyApplication) Commit() types.Result {
	hash := app.state.Hash()
	return types.NewResultOK(hash, "")
}

func (app *DummyApplication) Query(query []byte) types.Result {
	index, value, exists := app.state.Get(query)

	queryResult := QueryResult{index, string(value), exists}
	return types.NewResultOK(wire.JSONBytes(queryResult), "")
}

type QueryResult struct {
	Index  int    `json:"index"`
	Value  string `json:"value"`
	Exists bool   `json:"exists"`
}
<commit_msg>Encode dummy query values as hex strings<commit_after>package dummy

import (
	"encoding/hex"
	"strings"

	. "github.com/tendermint/go-common"
	"github.com/tendermint/go-merkle"
	"github.com/tendermint/go-wire"
	"github.com/tendermint/tmsp/types"
)

type DummyApplication struct {
	state merkle.Tree
}

func NewDummyApplication() *DummyApplication {
	state := merkle.NewIAVLTree(0, nil)
	return &DummyApplication{state: state}
}

func (app *DummyApplication) Info() (string, *types.TMSPInfo, *types.LastBlockInfo, *types.ConfigInfo) {
	return Fmt("{\"size\":%v}", app.state.Size()), nil, nil, nil
}

func (app *DummyApplication) SetOption(key string, value string) (log string) {
	return ""
}

// tx is either "key=value" or just arbitrary bytes
func (app *DummyApplication) AppendTx(tx []byte) types.Result {
	parts := strings.Split(string(tx), "=")
	if len(parts) == 2 {
		app.state.Set([]byte(parts[0]), []byte(parts[1]))
	} else {
		app.state.Set(tx, tx)
	}
	return types.OK
}

func (app *DummyApplication) CheckTx(tx []byte) types.Result {
	return types.OK
}

func (app *DummyApplication) Commit() types.Result {
	hash := app.state.Hash()
	return types.NewResultOK(hash, "")
}

func (app *DummyApplication) Query(query []byte) types.Result {
	index, value, exists := app.state.Get(query)
	queryResult := QueryResult{index, hex.EncodeToString(value), exists}
	return types.NewResultOK(wire.JSONBytes(queryResult), "")
}

type QueryResult struct {
	Index  int    `json:"index"`
	Value  string `json:"value"`
	Exists bool   `json:"exists"`
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

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 resource

import (
	"fmt"
	"io"
	"math/bits"

	"github.com/gogo/protobuf/proto"
)

var _ proto.Sizer = &Quantity{}

func (m *Quantity) Marshal() (data []byte, err error) {
	size := m.Size()
	data = make([]byte, size)
	n, err := m.MarshalToSizedBuffer(data[:size])
	if err != nil {
		return nil, err
	}
	return data[:n], nil
}

// MarshalTo is a customized version of the generated Protobuf unmarshaler for a struct
// with a single string field.
func (m *Quantity) MarshalTo(data []byte) (int, error) {
	size := m.Size()
	return m.MarshalToSizedBuffer(data[:size])
}

// MarshalToSizedBuffer is a customized version of the generated
// Protobuf unmarshaler for a struct with a single string field.
func (m *Quantity) MarshalToSizedBuffer(data []byte) (int, error) {
	i := len(data)
	_ = i
	var l int
	_ = l

	// BEGIN CUSTOM MARSHAL
	out := m.String()
	i -= len(out)
	copy(data[i:], out)
	i = encodeVarintGenerated(data, i, uint64(len(out)))
	// END CUSTOM MARSHAL
	i--
	data[i] = 0xa

	return len(data) - i, nil
}

func encodeVarintGenerated(data []byte, offset int, v uint64) int {
	offset -= sovGenerated(v)
	base := offset
	for v >= 1<<7 {
		data[offset] = uint8(v&0x7f | 0x80)
		v >>= 7
		offset++
	}
	data[offset] = uint8(v)
	return base
}

func (m *Quantity) Size() (n int) {
	var l int
	_ = l

	// BEGIN CUSTOM SIZE
	l = len(m.String())
	// END CUSTOM SIZE

	n += 1 + l + sovGenerated(uint64(l))
	return n
}

func sovGenerated(x uint64) (n int) {
	return (bits.Len64(x|1) + 6) / 7
}

// Unmarshal is a customized version of the generated Protobuf unmarshaler for a struct
// with a single string field.
func (m *Quantity) Unmarshal(data []byte) error {
	l := len(data)
	iNdEx := 0
	for iNdEx < l {
		preIndex := iNdEx
		var wire uint64
		for shift := uint(0); ; shift += 7 {
			if shift >= 64 {
				return ErrIntOverflowGenerated
			}
			if iNdEx >= l {
				return io.ErrUnexpectedEOF
			}
			b := data[iNdEx]
			iNdEx++
			wire |= (uint64(b) & 0x7F) << shift
			if b < 0x80 {
				break
			}
		}
		fieldNum := int32(wire >> 3)
		wireType := int(wire & 0x7)
		if wireType == 4 {
			return fmt.Errorf("proto: Quantity: wiretype end group for non-group")
		}
		if fieldNum <= 0 {
			return fmt.Errorf("proto: Quantity: illegal tag %d (wire type %d)", fieldNum, wire)
		}
		switch fieldNum {
		case 1:
			if wireType != 2 {
				return fmt.Errorf("proto: wrong wireType = %d for field String_", wireType)
			}
			var stringLen uint64
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowGenerated
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := data[iNdEx]
				iNdEx++
				stringLen |= (uint64(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
			intStringLen := int(stringLen)
			if intStringLen < 0 {
				return ErrInvalidLengthGenerated
			}
			postIndex := iNdEx + intStringLen
			if postIndex > l {
				return io.ErrUnexpectedEOF
			}
			s := string(data[iNdEx:postIndex])

			// BEGIN CUSTOM DECODE
			p, err := ParseQuantity(s)
			if err != nil {
				return err
			}
			*m = p
			// END CUSTOM DECODE

			iNdEx = postIndex
		default:
			iNdEx = preIndex
			skippy, err := skipGenerated(data[iNdEx:])
			if err != nil {
				return err
			}
			if skippy < 0 {
				return ErrInvalidLengthGenerated
			}
			if (iNdEx + skippy) > l {
				return io.ErrUnexpectedEOF
			}
			iNdEx += skippy
		}
	}

	if iNdEx > l {
		return io.ErrUnexpectedEOF
	}
	return nil
}

func skipGenerated(data []byte) (n int, err error) {
	l := len(data)
	iNdEx := 0
	for iNdEx < l {
		var wire uint64
		for shift := uint(0); ; shift += 7 {
			if shift >= 64 {
				return 0, ErrIntOverflowGenerated
			}
			if iNdEx >= l {
				return 0, io.ErrUnexpectedEOF
			}
			b := data[iNdEx]
			iNdEx++
			wire |= (uint64(b) & 0x7F) << shift
			if b < 0x80 {
				break
			}
		}
		wireType := int(wire & 0x7)
		switch wireType {
		case 0:
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return 0, ErrIntOverflowGenerated
				}
				if iNdEx >= l {
					return 0, io.ErrUnexpectedEOF
				}
				iNdEx++
				if data[iNdEx-1] < 0x80 {
					break
				}
			}
			return iNdEx, nil
		case 1:
			iNdEx += 8
			return iNdEx, nil
		case 2:
			var length int
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return 0, ErrIntOverflowGenerated
				}
				if iNdEx >= l {
					return 0, io.ErrUnexpectedEOF
				}
				b := data[iNdEx]
				iNdEx++
				length |= (int(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
			iNdEx += length
			if length < 0 {
				return 0, ErrInvalidLengthGenerated
			}
			return iNdEx, nil
		case 3:
			for {
				var innerWire uint64
				var start int = iNdEx
				for shift := uint(0); ; shift += 7 {
					if shift >= 64 {
						return 0, ErrIntOverflowGenerated
					}
					if iNdEx >= l {
						return 0, io.ErrUnexpectedEOF
					}
					b := data[iNdEx]
					iNdEx++
					innerWire |= (uint64(b) & 0x7F) << shift
					if b < 0x80 {
						break
					}
				}
				innerWireType := int(innerWire & 0x7)
				if innerWireType == 4 {
					break
				}
				next, err := skipGenerated(data[start:])
				if err != nil {
					return 0, err
				}
				iNdEx = start + next
			}
			return iNdEx, nil
		case 4:
			return iNdEx, nil
		case 5:
			iNdEx += 4
			return iNdEx, nil
		default:
			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
		}
	}
	panic("unreachable")
}

var (
	ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling")
	ErrIntOverflowGenerated   = fmt.Errorf("proto: integer overflow")
)
<commit_msg>Additional CVE-2021-3121 fix<commit_after>/*
Copyright 2015 The Kubernetes Authors.

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 resource

import (
	"fmt"
	"io"
	"math/bits"

	"github.com/gogo/protobuf/proto"
)

var _ proto.Sizer = &Quantity{}

func (m *Quantity) Marshal() (data []byte, err error) {
	size := m.Size()
	data = make([]byte, size)
	n, err := m.MarshalToSizedBuffer(data[:size])
	if err != nil {
		return nil, err
	}
	return data[:n], nil
}

// MarshalTo is a customized version of the generated Protobuf unmarshaler for a struct
// with a single string field.
func (m *Quantity) MarshalTo(data []byte) (int, error) {
	size := m.Size()
	return m.MarshalToSizedBuffer(data[:size])
}

// MarshalToSizedBuffer is a customized version of the generated
// Protobuf unmarshaler for a struct with a single string field.
func (m *Quantity) MarshalToSizedBuffer(data []byte) (int, error) {
	i := len(data)
	_ = i
	var l int
	_ = l

	// BEGIN CUSTOM MARSHAL
	out := m.String()
	i -= len(out)
	copy(data[i:], out)
	i = encodeVarintGenerated(data, i, uint64(len(out)))
	// END CUSTOM MARSHAL
	i--
	data[i] = 0xa

	return len(data) - i, nil
}

func encodeVarintGenerated(data []byte, offset int, v uint64) int {
	offset -= sovGenerated(v)
	base := offset
	for v >= 1<<7 {
		data[offset] = uint8(v&0x7f | 0x80)
		v >>= 7
		offset++
	}
	data[offset] = uint8(v)
	return base
}

func (m *Quantity) Size() (n int) {
	var l int
	_ = l

	// BEGIN CUSTOM SIZE
	l = len(m.String())
	// END CUSTOM SIZE

	n += 1 + l + sovGenerated(uint64(l))
	return n
}

func sovGenerated(x uint64) (n int) {
	return (bits.Len64(x|1) + 6) / 7
}

// Unmarshal is a customized version of the generated Protobuf unmarshaler for a struct
// with a single string field.
func (m *Quantity) Unmarshal(data []byte) error {
	l := len(data)
	iNdEx := 0
	for iNdEx < l {
		preIndex := iNdEx
		var wire uint64
		for shift := uint(0); ; shift += 7 {
			if shift >= 64 {
				return ErrIntOverflowGenerated
			}
			if iNdEx >= l {
				return io.ErrUnexpectedEOF
			}
			b := data[iNdEx]
			iNdEx++
			wire |= (uint64(b) & 0x7F) << shift
			if b < 0x80 {
				break
			}
		}
		fieldNum := int32(wire >> 3)
		wireType := int(wire & 0x7)
		if wireType == 4 {
			return fmt.Errorf("proto: Quantity: wiretype end group for non-group")
		}
		if fieldNum <= 0 {
			return fmt.Errorf("proto: Quantity: illegal tag %d (wire type %d)", fieldNum, wire)
		}
		switch fieldNum {
		case 1:
			if wireType != 2 {
				return fmt.Errorf("proto: wrong wireType = %d for field String_", wireType)
			}
			var stringLen uint64
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowGenerated
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := data[iNdEx]
				iNdEx++
				stringLen |= (uint64(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
			intStringLen := int(stringLen)
			if intStringLen < 0 {
				return ErrInvalidLengthGenerated
			}
			postIndex := iNdEx + intStringLen
			if postIndex > l {
				return io.ErrUnexpectedEOF
			}
			s := string(data[iNdEx:postIndex])

			// BEGIN CUSTOM DECODE
			p, err := ParseQuantity(s)
			if err != nil {
				return err
			}
			*m = p
			// END CUSTOM DECODE

			iNdEx = postIndex
		default:
			iNdEx = preIndex
			skippy, err := skipGenerated(data[iNdEx:])
			if err != nil {
				return err
			}
			if (skippy < 0) || (iNdEx+skippy) < 0 {
				return ErrInvalidLengthGenerated
			}
			if (iNdEx + skippy) > l {
				return io.ErrUnexpectedEOF
			}
			iNdEx += skippy
		}
	}

	if iNdEx > l {
		return io.ErrUnexpectedEOF
	}
	return nil
}

func skipGenerated(data []byte) (n int, err error) {
	l := len(data)
	iNdEx := 0
	for iNdEx < l {
		var wire uint64
		for shift := uint(0); ; shift += 7 {
			if shift >= 64 {
				return 0, ErrIntOverflowGenerated
			}
			if iNdEx >= l {
				return 0, io.ErrUnexpectedEOF
			}
			b := data[iNdEx]
			iNdEx++
			wire |= (uint64(b) & 0x7F) << shift
			if b < 0x80 {
				break
			}
		}
		wireType := int(wire & 0x7)
		switch wireType {
		case 0:
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return 0, ErrIntOverflowGenerated
				}
				if iNdEx >= l {
					return 0, io.ErrUnexpectedEOF
				}
				iNdEx++
				if data[iNdEx-1] < 0x80 {
					break
				}
			}
			return iNdEx, nil
		case 1:
			iNdEx += 8
			return iNdEx, nil
		case 2:
			var length int
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return 0, ErrIntOverflowGenerated
				}
				if iNdEx >= l {
					return 0, io.ErrUnexpectedEOF
				}
				b := data[iNdEx]
				iNdEx++
				length |= (int(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
			iNdEx += length
			if length < 0 {
				return 0, ErrInvalidLengthGenerated
			}
			return iNdEx, nil
		case 3:
			for {
				var innerWire uint64
				var start int = iNdEx
				for shift := uint(0); ; shift += 7 {
					if shift >= 64 {
						return 0, ErrIntOverflowGenerated
					}
					if iNdEx >= l {
						return 0, io.ErrUnexpectedEOF
					}
					b := data[iNdEx]
					iNdEx++
					innerWire |= (uint64(b) & 0x7F) << shift
					if b < 0x80 {
						break
					}
				}
				innerWireType := int(innerWire & 0x7)
				if innerWireType == 4 {
					break
				}
				next, err := skipGenerated(data[start:])
				if err != nil {
					return 0, err
				}
				iNdEx = start + next
			}
			return iNdEx, nil
		case 4:
			return iNdEx, nil
		case 5:
			iNdEx += 4
			return iNdEx, nil
		default:
			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
		}
	}
	panic("unreachable")
}

var (
	ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling")
	ErrIntOverflowGenerated   = fmt.Errorf("proto: integer overflow")
)
<|endoftext|>


<commit_before>package vnfm

import (
	"errors"
	"fmt"
	"os"
	"runtime/debug"
	"sync"
	"time"

	"github.com/mcilloni/go-openbaton/catalogue/messages"
	"github.com/mcilloni/go-openbaton/vnfm/channel"
	"github.com/mcilloni/go-openbaton/vnfm/config"
	log "github.com/sirupsen/logrus"
)

var impls = make(map[string]channel.Driver)

func Register(name string, driver channel.Driver) {
	if _, ok := impls[name]; ok {
		panic(fmt.Sprintf("trying to register driver of type %T with already existing name '%s'", driver, name))
	}

	if driver == nil {
		panic("nil driver")
	}

	impls[name] = driver
}

type VNFM interface {
	Logger() *log.Logger
	Serve() error
	Stop() error
}

func New(implName string, handler Handler, config *config.Config) (VNFM, error) {
	if _, ok := impls[implName]; !ok {
		return nil, fmt.Errorf("no implementation available for %s. Have you forgot to import its package?", implName)
	}

	logger := log.New()

	logger.Formatter = &log.TextFormatter{
		ForceColors:   config.LogColors,
		DisableColors: !config.LogColors,
	}

	logger.Level = config.LogLevel

	if config.LogFile != "" {
		file, err := os.OpenFile(config.LogFile, os.O_APPEND | os.O_WRONLY | os.O_CREATE, os.ModeAppend)
		if err != nil {
			return nil, fmt.Errorf("couldn't open the log file %s: %s", config.LogFile, err.Error())
		}

		logger.Out = file
	}

	return &vnfm{
		hnd:      handler,
		implName: implName,
		conf:     config,
		l:        logger,
		quitChan: make(chan error, 1), // do not block on send
	}, nil
}

type vnfm struct {
	cnl      channel.Channel
	conf     *config.Config
	hnd      Handler
	implName string
	l        *log.Logger
	msgChan  <-chan messages.NFVMessage
	quitChan chan error
	wg       sync.WaitGroup
}

func (vnfm *vnfm) Logger() *log.Logger {
	return vnfm.l
}

func (vnfm *vnfm) Serve() (err error) {
	if vnfm.cnl, err = impls[vnfm.implName].Init(vnfm.conf, vnfm.l); err != nil {
		return
	}

	defer func() {
		r := recover()

		// If it's not stderr, it's the file we opened in New.
		if vnfm.l.Out != os.Stderr {
			vnfm.l.Out.(*os.File).Close()
		}

		// answering the channel signals Stop() that we're quitting
		err := vnfm.cnl.Close()

		if err == nil {
			// if the channel closed politely, the workers will be quitting by now;
			// otherwise, they will be killed when main exits.
			vnfm.wg.Wait()
	  	}

		vnfm.quitChan <- err

		if r != nil {
			vnfm.l.WithFields(log.Fields{
				"tag":         "vnfm-serve-on_exit",
				"stack-trace": string(debug.Stack()),
			}).Panic(r)
		}
	}()

	if vnfm.msgChan, err = vnfm.cnl.NotifyReceived(); err != nil {
		return
	}

	vnfm.spawnWorkers()

	// wait for Stop()
	<-vnfm.quitChan

	return
}

func (vnfm *vnfm) SetLogger(log *log.Logger) {
	vnfm.l = log
}

func (vnfm *vnfm) Stop() error {
	select {
	case vnfm.quitChan <- nil:

	case <-time.After(time.Second):
		return errors.New("the VNFM is not listening")
	}

	select {
	case err := <-vnfm.quitChan:
		return err
	case <-time.After(1 * time.Minute):
		return errors.New("the VNFM refused to quit")
	}
}

func (vnfm *vnfm) spawnWorkers() {
	const NumWorkers = 5

	vnfm.wg.Add(NumWorkers)

	for i := 0; i < NumWorkers; i++ {
		go (&worker{vnfm, i}).spawn()
	}
}
<commit_msg>Colour fixes<commit_after>package vnfm

import (
	"errors"
	"fmt"
	"os"
	"runtime/debug"
	"sync"
	"time"

	"github.com/mcilloni/go-openbaton/catalogue/messages"
	"github.com/mcilloni/go-openbaton/vnfm/channel"
	"github.com/mcilloni/go-openbaton/vnfm/config"
	log "github.com/sirupsen/logrus"
)

var impls = make(map[string]channel.Driver)

func Register(name string, driver channel.Driver) {
	if _, ok := impls[name]; ok {
		panic(fmt.Sprintf("trying to register driver of type %T with already existing name '%s'", driver, name))
	}

	if driver == nil {
		panic("nil driver")
	}

	impls[name] = driver
}

type VNFM interface {
	Logger() *log.Logger
	Serve() error
	Stop() error
}

func New(implName string, handler Handler, config *config.Config) (VNFM, error) {
	if _, ok := impls[implName]; !ok {
		return nil, fmt.Errorf("no implementation available for %s. Have you forgot to import its package?", implName)
	}

	logger := log.New()

	logger.Formatter = &log.TextFormatter{
		ForceColors:   config.LogColors,
		DisableColors: !config.LogColors,
	}

	logger.Level = config.LogLevel

	if config.LogFile != "" {
		file, err := os.OpenFile(config.LogFile, os.O_APPEND | os.O_WRONLY | os.O_CREATE, os.ModeAppend)
		if err != nil {
			return nil, fmt.Errorf("couldn't open the log file %s: %s", config.LogFile, err.Error())
		}

		logger.Out = file
	} else {
		logger.Out = terminalWriter()
	}

	return &vnfm{
		hnd:      handler,
		implName: implName,
		conf:     config,
		l:        logger,
		quitChan: make(chan error, 1), // do not block on send
	}, nil
}

type vnfm struct {
	cnl      channel.Channel
	conf     *config.Config
	hnd      Handler
	implName string
	l        *log.Logger
	msgChan  <-chan messages.NFVMessage
	quitChan chan error
	wg       sync.WaitGroup
}

func (vnfm *vnfm) Logger() *log.Logger {
	return vnfm.l
}

func (vnfm *vnfm) Serve() (err error) {
	if vnfm.cnl, err = impls[vnfm.implName].Init(vnfm.conf, vnfm.l); err != nil {
		return
	}

	defer func() {
		r := recover()

		// Check if if a file has been opened in New.
		if file, ok := vnfm.l.Out.(*os.File); ok {
			if err := file.Close(); err != nil {
				fmt.Fprintf(os.Stderr, "error while closing logfile: %v\n", err)
			}
		}

		// answering the channel signals Stop() that we're quitting
		err := vnfm.cnl.Close()

		if err == nil {
			// if the channel closed politely, the workers will be quitting by now;
			// otherwise, they will be killed when main exits.
			vnfm.wg.Wait()
	  	}

		vnfm.quitChan <- err

		if r != nil {
			vnfm.l.WithFields(log.Fields{
				"tag":         "vnfm-serve-on_exit",
				"stack-trace": string(debug.Stack()),
			}).Panic(r)
		}
	}()

	if vnfm.msgChan, err = vnfm.cnl.NotifyReceived(); err != nil {
		return
	}

	vnfm.spawnWorkers()

	// wait for Stop()
	<-vnfm.quitChan

	return
}

func (vnfm *vnfm) SetLogger(log *log.Logger) {
	vnfm.l = log
}

func (vnfm *vnfm) Stop() error {
	select {
	case vnfm.quitChan <- nil:

	case <-time.After(time.Second):
		return errors.New("the VNFM is not listening")
	}

	select {
	case err := <-vnfm.quitChan:
		return err
	case <-time.After(1 * time.Minute):
		return errors.New("the VNFM refused to quit")
	}
}

func (vnfm *vnfm) spawnWorkers() {
	const NumWorkers = 5

	vnfm.wg.Add(NumWorkers)

	for i := 0; i < NumWorkers; i++ {
		go (&worker{vnfm, i}).spawn()
	}
}
<|endoftext|>


<commit_before>package ground

import (
	"fmt"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	clientset "k8s.io/client-go/kubernetes"
	rbac "k8s.io/client-go/pkg/apis/rbac/v1beta1"
)

func SeedKluster(client clientset.Interface) error {
	if err := SeedAllowBootstrapTokensToPostCSRs(client); err != nil {
		return err
	}
	if err := SeedAutoApproveNodeBootstrapTokens(client); err != nil {
		return err
	}
	return nil

}

func SeedAllowBootstrapTokensToPostCSRs(client clientset.Interface) error {
	return CreateOrUpdateClusterRoleBinding(client, &rbac.ClusterRoleBinding{
		ObjectMeta: metav1.ObjectMeta{
			Name: "kubernikus:kubelet-bootstrap",
		},
		RoleRef: rbac.RoleRef{
			APIGroup: rbac.GroupName,
			Kind:     "ClusterRole",
			Name:     "system:node-bootstrapper",
		},
		Subjects: []rbac.Subject{
			{
				Kind: rbac.GroupKind,
				Name: "system:bootstrappers",
			},
		},
	})
}

func SeedAutoApproveNodeBootstrapTokens(client clientset.Interface) error {
	err := CreateOrUpdateClusterRole(client, &rbac.ClusterRole{
		ObjectMeta: metav1.ObjectMeta{
			Name: "kubernikus:approve-node-client-csr",
		},
		Rules: []rbac.PolicyRule{
			rbac.NewRule("create").Groups("certificates.k8s.io").Resources("certificatesigningrequests/nodeclient").RuleOrDie(),
		},
	})
	if err != nil {
		return err
	}

	return CreateOrUpdateClusterRoleBinding(client, &rbac.ClusterRoleBinding{
		ObjectMeta: metav1.ObjectMeta{
			Name: "kubernikus:node-client-csr-autoapprove",
		},
		RoleRef: rbac.RoleRef{
			APIGroup: rbac.GroupName,
			Kind:     "ClusterRole",
			Name:     "kubernikus:kubelet-bootstrap",
		},
		Subjects: []rbac.Subject{
			{
				Kind: "Group",
				Name: "system:bootstrappers",
			},
		},
	})
}

func CreateOrUpdateClusterRoleBinding(client clientset.Interface, clusterRoleBinding *rbac.ClusterRoleBinding) error {
	if _, err := client.RbacV1beta1().ClusterRoleBindings().Create(clusterRoleBinding); err != nil {
		if !apierrors.IsAlreadyExists(err) {
			return fmt.Errorf("unable to create RBAC clusterrolebinding: %v", err)
		}

		if _, err := client.RbacV1beta1().ClusterRoleBindings().Update(clusterRoleBinding); err != nil {
			return fmt.Errorf("unable to update RBAC clusterrolebinding: %v", err)
		}
	}
	return nil
}

func CreateOrUpdateClusterRole(client clientset.Interface, clusterRole *rbac.ClusterRole) error {
	if _, err := client.RbacV1beta1().ClusterRoles().Create(clusterRole); err != nil {
		if !apierrors.IsAlreadyExists(err) {
			return fmt.Errorf("unable to create RBAC clusterrole: %v", err)
		}

		if _, err := client.RbacV1beta1().ClusterRoles().Update(clusterRole); err != nil {
			return fmt.Errorf("unable to update RBAC clusterrole: %v", err)
		}
	}
	return nil
}
<commit_msg>actually bind the correct role<commit_after>package ground

import (
	"fmt"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	clientset "k8s.io/client-go/kubernetes"
	rbac "k8s.io/client-go/pkg/apis/rbac/v1beta1"
)

func SeedKluster(client clientset.Interface) error {
	if err := SeedAllowBootstrapTokensToPostCSRs(client); err != nil {
		return err
	}
	if err := SeedAutoApproveNodeBootstrapTokens(client); err != nil {
		return err
	}
	return nil

}

func SeedAllowBootstrapTokensToPostCSRs(client clientset.Interface) error {
	return CreateOrUpdateClusterRoleBinding(client, &rbac.ClusterRoleBinding{
		ObjectMeta: metav1.ObjectMeta{
			Name: "kubernikus:kubelet-bootstrap",
		},
		RoleRef: rbac.RoleRef{
			APIGroup: rbac.GroupName,
			Kind:     "ClusterRole",
			Name:     "system:node-bootstrapper",
		},
		Subjects: []rbac.Subject{
			{
				Kind: rbac.GroupKind,
				Name: "system:bootstrappers",
			},
		},
	})
}

func SeedAutoApproveNodeBootstrapTokens(client clientset.Interface) error {
	err := CreateOrUpdateClusterRole(client, &rbac.ClusterRole{
		ObjectMeta: metav1.ObjectMeta{
			Name: "kubernikus:approve-node-client-csr",
		},
		Rules: []rbac.PolicyRule{
			rbac.NewRule("create").Groups("certificates.k8s.io").Resources("certificatesigningrequests/nodeclient").RuleOrDie(),
		},
	})
	if err != nil {
		return err
	}

	return CreateOrUpdateClusterRoleBinding(client, &rbac.ClusterRoleBinding{
		ObjectMeta: metav1.ObjectMeta{
			Name: "kubernikus:node-client-csr-autoapprove",
		},
		RoleRef: rbac.RoleRef{
			APIGroup: rbac.GroupName,
			Kind:     "ClusterRole",
			Name:     "kubernikus:approve-node-client-csr",
		},
		Subjects: []rbac.Subject{
			{
				Kind: "Group",
				Name: "system:bootstrappers",
			},
		},
	})
}

func CreateOrUpdateClusterRoleBinding(client clientset.Interface, clusterRoleBinding *rbac.ClusterRoleBinding) error {
	if _, err := client.RbacV1beta1().ClusterRoleBindings().Create(clusterRoleBinding); err != nil {
		if !apierrors.IsAlreadyExists(err) {
			return fmt.Errorf("unable to create RBAC clusterrolebinding: %v", err)
		}

		if _, err := client.RbacV1beta1().ClusterRoleBindings().Update(clusterRoleBinding); err != nil {
			return fmt.Errorf("unable to update RBAC clusterrolebinding: %v", err)
		}
	}
	return nil
}

func CreateOrUpdateClusterRole(client clientset.Interface, clusterRole *rbac.ClusterRole) error {
	if _, err := client.RbacV1beta1().ClusterRoles().Create(clusterRole); err != nil {
		if !apierrors.IsAlreadyExists(err) {
			return fmt.Errorf("unable to create RBAC clusterrole: %v", err)
		}

		if _, err := client.RbacV1beta1().ClusterRoles().Update(clusterRole); err != nil {
			return fmt.Errorf("unable to update RBAC clusterrole: %v", err)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package storage

import (
	"fmt"
	"strings"

	"github.com/flant/lockgate"

	"github.com/flant/werf/pkg/werf"

	"github.com/flant/werf/pkg/image"

	"github.com/flant/werf/pkg/container_runtime"
	"github.com/flant/werf/pkg/docker"

	"github.com/flant/logboek"
	"github.com/flant/werf/pkg/docker_registry"
)

const (
	RepoStage_ImageFormat = "%s:%s-%d"

	RepoManagedImageRecord_ImageTagPrefix  = "managed-image-"
	RepoManagedImageRecord_ImageNameFormat = "%s:managed-image-%s"
)

func getSignatureAndUniqueIDFromRepoStageImageTag(repoStageImageTag string) (string, int64, error) {
	parts := strings.SplitN(repoStageImageTag, "-", 2)
	if uniqueID, err := image.ParseUniqueIDAsTimestamp(parts[1]); err != nil {
		return "", 0, fmt.Errorf("unable to parse unique id %s as timestamp: %s", parts[1], err)
	} else {
		return parts[0], uniqueID, nil
	}
}

type RepoStagesStorage struct {
	RepoAddress      string
	DockerRegistry   docker_registry.DockerRegistry
	ContainerRuntime container_runtime.ContainerRuntime
}

type RepoStagesStorageOptions struct {
	docker_registry.DockerRegistryOptions
	Implementation string
}

func NewRepoStagesStorage(repoAddress string, containerRuntime container_runtime.ContainerRuntime, options RepoStagesStorageOptions) (*RepoStagesStorage, error) {
	implementation := options.Implementation

	dockerRegistry, err := docker_registry.NewDockerRegistry(repoAddress, implementation, options.DockerRegistryOptions)
	if err != nil {
		return nil, fmt.Errorf("error creating docker registry accessor for repo %q: %s", repoAddress, err)
	}

	return &RepoStagesStorage{
		RepoAddress:      repoAddress,
		DockerRegistry:   dockerRegistry,
		ContainerRuntime: containerRuntime,
	}, nil
}

func (storage *RepoStagesStorage) ConstructStageImageName(projectName, signature string, uniqueID int64) string {
	return fmt.Sprintf(RepoStage_ImageFormat, storage.RepoAddress, signature, uniqueID)
}

func (storage *RepoStagesStorage) GetAllStages(projectName string) ([]image.StageID, error) {
	var res []image.StageID

	if tags, err := storage.DockerRegistry.Tags(storage.RepoAddress); err != nil {
		return nil, fmt.Errorf("unable to fetch tags for repo %q: %s", storage.RepoAddress, err)
	} else {
		logboek.Debug.LogF("-- RepoStagesStorage.GetRepoImagesBySignature fetched tags for %q: %#v\n", storage.RepoAddress, tags)

		for _, tag := range tags {
			if strings.HasPrefix(tag, RepoManagedImageRecord_ImageTagPrefix) {
				continue
			}

			if signature, uniqueID, err := getSignatureAndUniqueIDFromRepoStageImageTag(tag); err != nil {
				return nil, err
			} else {
				res = append(res, image.StageID{Signature: signature, UniqueID: uniqueID})

				logboek.Debug.LogF("Selected stage by signature %q uniqueID %d\n", signature, uniqueID)
			}
		}

		return res, nil
	}
}

func (storage *RepoStagesStorage) DeleteStages(options DeleteImageOptions, stages ...*image.StageDescription) error {
	var imageInfoList []*image.Info
	for _, stageDesc := range stages {
		imageInfoList = append(imageInfoList, stageDesc.Info)
	}
	return storage.DockerRegistry.DeleteRepoImage(imageInfoList...)
}

func (storage *RepoStagesStorage) CreateRepo() error {
	return storage.DockerRegistry.CreateRepo(storage.RepoAddress)
}

func (storage *RepoStagesStorage) DeleteRepo() error {
	return storage.DockerRegistry.DeleteRepo(storage.RepoAddress)
}

func (storage *RepoStagesStorage) GetStagesBySignature(projectName, signature string) ([]image.StageID, error) {
	var res []image.StageID

	if tags, err := storage.DockerRegistry.Tags(storage.RepoAddress); err != nil {
		return nil, fmt.Errorf("unable to fetch tags for repo %q: %s", storage.RepoAddress, err)
	} else {
		logboek.Debug.LogF("-- RepoStagesStorage.GetRepoImagesBySignature fetched tags for %q: %#v\n", storage.RepoAddress, tags)
		for _, tag := range tags {
			if !strings.HasPrefix(tag, signature) {
				logboek.Debug.LogF("Discard tag %q: should have prefix %q\n", tag, signature)
				continue
			}
			if _, uniqueID, err := getSignatureAndUniqueIDFromRepoStageImageTag(tag); err != nil {
				return nil, err
			} else {
				logboek.Debug.LogF("Tag %q is suitable for signature %q\n", tag, signature)
				res = append(res, image.StageID{Signature: signature, UniqueID: uniqueID})
			}
		}
	}

	logboek.Debug.LogF("-- RepoStagesStorage.GetRepoImagesBySignature result for %q: %#v\n", storage.RepoAddress, res)

	return res, nil
}

func (storage *RepoStagesStorage) GetStageDescription(projectName, signature string, uniqueID int64) (*image.StageDescription, error) {
	stageImageName := storage.ConstructStageImageName(projectName, signature, uniqueID)

	logboek.Debug.LogF("-- RepoStagesStorage GetStageDescription %s %s %d\n", projectName, signature, uniqueID)
	logboek.Debug.LogF("-- RepoStagesStorage stageImageName = %q\n", stageImageName)

	if imgInfo, err := storage.DockerRegistry.TryGetRepoImage(stageImageName); err != nil {
		return nil, err
	} else if imgInfo != nil {
		return &image.StageDescription{
			StageID: &image.StageID{Signature: signature, UniqueID: uniqueID},
			Info:    imgInfo,
		}, nil
	}
	return nil, nil
}

func (storage *RepoStagesStorage) AddManagedImage(projectName, imageName string) error {
	logboek.Debug.LogF("-- RepoStagesStorage.AddManagedImage %s %s\n", projectName, imageName)

	fullImageName := makeRepoManagedImageRecord(storage.RepoAddress, imageName)

	if _, lock, err := werf.AcquireHostLock(fmt.Sprintf("managed_image.%s-%s", projectName, imageName), lockgate.AcquireOptions{}); err != nil {
		return err
	} else {
		defer werf.ReleaseHostLock(lock)
	}

	if isExists, err := storage.DockerRegistry.IsRepoImageExists(fullImageName); err != nil {
		return err
	} else if isExists {
		logboek.Debug.LogF("-- RepoStagesStorage.AddManagedImage record %q is exists => exiting\n", fullImageName)
		return nil
	}

	logboek.Debug.LogF("-- RepoStagesStorage.AddManagedImage record %q does not exist => creating record\n", fullImageName)

	switch storage.ContainerRuntime.(type) {
	case *container_runtime.LocalDockerServerRuntime:
		if err := docker.CreateImage(fullImageName); err != nil {
			return fmt.Errorf("unable to create image %q: %s", fullImageName, err)
		}
		defer func() {
			if err := docker.CliRmi("--force", fullImageName); err != nil {
				// TODO: errored repo state
				logboek.Error.LogF("unable to remove temporary image %q: %s", fullImageName, err)
			}
		}()

		if err := docker.CliPushWithRetries(fullImageName); err != nil {
			return fmt.Errorf("unable to push image %q: %s", fullImageName, err)
		}

		return nil
	default: // TODO: case *container_runtime.LocalHostRuntime:
		panic("not implemented")
	}
}

func (storage *RepoStagesStorage) RmManagedImage(projectName, imageName string) error {
	logboek.Debug.LogF("-- RepoStagesStorage.RmManagedImage %s %s\n", projectName, imageName)

	fullImageName := makeRepoManagedImageRecord(storage.RepoAddress, imageName)

	if imgInfo, err := storage.DockerRegistry.TryGetRepoImage(fullImageName); err != nil {
		return fmt.Errorf("unable to get repo image %q info: %s", fullImageName, err)
	} else if imgInfo == nil {
		logboek.Debug.LogF("-- RepoStagesStorage.RmManagedImage record %q does not exist => exiting\n", fullImageName)
		return nil
	} else {
		if err := storage.DockerRegistry.DeleteRepoImage(imgInfo); err != nil {
			return fmt.Errorf("unable to delete image %q from repo: %s", fullImageName, err)
		}
	}

	return nil
}

func (storage *RepoStagesStorage) GetManagedImages(projectName string) ([]string, error) {
	logboek.Debug.LogF("-- RepoStagesStorage.GetManagedImages %s\n", projectName)

	var res []string

	if tags, err := storage.DockerRegistry.Tags(storage.RepoAddress); err != nil {
		return nil, fmt.Errorf("unable to get repo %q tags: %s", storage.RepoAddress, err)
	} else {
		for _, tag := range tags {
			if !strings.HasPrefix(tag, RepoManagedImageRecord_ImageTagPrefix) {
				continue
			}

			tag = strings.ReplaceAll(tag, "__slash__", "/")
			tag = strings.ReplaceAll(tag, "__plus__", "+")

			managedImageName := strings.TrimPrefix(tag, RepoManagedImageRecord_ImageTagPrefix)
			if managedImageName == NamelessImageRecordTag {
				res = append(res, "")
			} else {
				res = append(res, managedImageName)
			}
		}
	}

	return res, nil
}

func (storage *RepoStagesStorage) FetchImage(img container_runtime.Image) error {
	switch containerRuntime := storage.ContainerRuntime.(type) {
	case *container_runtime.LocalDockerServerRuntime:
		return containerRuntime.PullImageFromRegistry(img)
	default:
		// TODO: case *container_runtime.LocalHostRuntime:
		panic("not implemented")
	}
}

func (storage *RepoStagesStorage) StoreImage(img container_runtime.Image) error {
	switch containerRuntime := storage.ContainerRuntime.(type) {
	case *container_runtime.LocalDockerServerRuntime:
		dockerImage := img.(*container_runtime.DockerImage)

		if dockerImage.Image.GetBuiltId() != "" {
			return containerRuntime.PushBuiltImage(img)
		} else {
			return containerRuntime.PushImage(img)
		}

	default:
		// TODO: case *container_runtime.LocalHostRuntime:
		panic("not implemented")
	}
}

func (storage *RepoStagesStorage) ShouldFetchImage(img container_runtime.Image) (bool, error) {
	switch storage.ContainerRuntime.(type) {
	case *container_runtime.LocalDockerServerRuntime:
		dockerImage := img.(*container_runtime.DockerImage)
		return !dockerImage.Image.IsExistsLocally(), nil
	default:
		panic("not implemented")
	}
}

func (storage *RepoStagesStorage) String() string {
	return fmt.Sprintf("repo stages storage (%q)", storage.RepoAddress)
}

func (storage *RepoStagesStorage) Address() string {
	return storage.RepoAddress
}

func makeRepoManagedImageRecord(repoAddress, imageName string) string {
	tagSuffix := imageName
	if imageName == "" {
		tagSuffix = NamelessImageRecordTag
	}

	tagSuffix = strings.ReplaceAll(tagSuffix, "/", "__slash__")
	tagSuffix = strings.ReplaceAll(tagSuffix, "+", "__plus__")

	return fmt.Sprintf(RepoManagedImageRecord_ImageNameFormat, repoAddress, tagSuffix)
}
<commit_msg>Ignore stages storage tags not matching the expected format<commit_after>package storage

import (
	"fmt"
	"strings"

	"github.com/flant/lockgate"

	"github.com/flant/werf/pkg/werf"

	"github.com/flant/werf/pkg/image"

	"github.com/flant/werf/pkg/container_runtime"
	"github.com/flant/werf/pkg/docker"

	"github.com/flant/logboek"
	"github.com/flant/werf/pkg/docker_registry"
)

const (
	RepoStage_ImageFormat = "%s:%s-%d"

	RepoManagedImageRecord_ImageTagPrefix  = "managed-image-"
	RepoManagedImageRecord_ImageNameFormat = "%s:managed-image-%s"

	UnexpectedTagFormatErrorPrefix = "unexpected tag format"
)

func getSignatureAndUniqueIDFromRepoStageImageTag(repoStageImageTag string) (string, int64, error) {
	parts := strings.SplitN(repoStageImageTag, "-", 2)

	if len(parts) != 2 {
		return "", 0, fmt.Errorf("%s %s", UnexpectedTagFormatErrorPrefix, repoStageImageTag)
	}

	if uniqueID, err := image.ParseUniqueIDAsTimestamp(parts[1]); err != nil {
		return "", 0, fmt.Errorf("%s %s: unable to parse unique id %s as timestamp: %s", UnexpectedTagFormatErrorPrefix, repoStageImageTag, parts[1], err)
	} else {
		return parts[0], uniqueID, nil
	}
}

func isUnexpectedTagFormatError(err error) bool {
	return strings.HasPrefix(err.Error(), UnexpectedTagFormatErrorPrefix)
}

type RepoStagesStorage struct {
	RepoAddress      string
	DockerRegistry   docker_registry.DockerRegistry
	ContainerRuntime container_runtime.ContainerRuntime
}

type RepoStagesStorageOptions struct {
	docker_registry.DockerRegistryOptions
	Implementation string
}

func NewRepoStagesStorage(repoAddress string, containerRuntime container_runtime.ContainerRuntime, options RepoStagesStorageOptions) (*RepoStagesStorage, error) {
	implementation := options.Implementation

	dockerRegistry, err := docker_registry.NewDockerRegistry(repoAddress, implementation, options.DockerRegistryOptions)
	if err != nil {
		return nil, fmt.Errorf("error creating docker registry accessor for repo %q: %s", repoAddress, err)
	}

	return &RepoStagesStorage{
		RepoAddress:      repoAddress,
		DockerRegistry:   dockerRegistry,
		ContainerRuntime: containerRuntime,
	}, nil
}

func (storage *RepoStagesStorage) ConstructStageImageName(projectName, signature string, uniqueID int64) string {
	return fmt.Sprintf(RepoStage_ImageFormat, storage.RepoAddress, signature, uniqueID)
}

func (storage *RepoStagesStorage) GetAllStages(projectName string) ([]image.StageID, error) {
	var res []image.StageID

	if tags, err := storage.DockerRegistry.Tags(storage.RepoAddress); err != nil {
		return nil, fmt.Errorf("unable to fetch tags for repo %q: %s", storage.RepoAddress, err)
	} else {
		logboek.Debug.LogF("-- RepoStagesStorage.GetRepoImagesBySignature fetched tags for %q: %#v\n", storage.RepoAddress, tags)

		for _, tag := range tags {
			if strings.HasPrefix(tag, RepoManagedImageRecord_ImageTagPrefix) {
				continue
			}

			if signature, uniqueID, err := getSignatureAndUniqueIDFromRepoStageImageTag(tag); err != nil {
				if isUnexpectedTagFormatError(err) {
					logboek.Debug.LogLn(strings.Title(err.Error()))
					continue
				}
				return nil, err
			} else {
				res = append(res, image.StageID{Signature: signature, UniqueID: uniqueID})

				logboek.Debug.LogF("Selected stage by signature %q uniqueID %d\n", signature, uniqueID)
			}
		}

		return res, nil
	}
}

func (storage *RepoStagesStorage) DeleteStages(options DeleteImageOptions, stages ...*image.StageDescription) error {
	var imageInfoList []*image.Info
	for _, stageDesc := range stages {
		imageInfoList = append(imageInfoList, stageDesc.Info)
	}
	return storage.DockerRegistry.DeleteRepoImage(imageInfoList...)
}

func (storage *RepoStagesStorage) CreateRepo() error {
	return storage.DockerRegistry.CreateRepo(storage.RepoAddress)
}

func (storage *RepoStagesStorage) DeleteRepo() error {
	return storage.DockerRegistry.DeleteRepo(storage.RepoAddress)
}

func (storage *RepoStagesStorage) GetStagesBySignature(projectName, signature string) ([]image.StageID, error) {
	var res []image.StageID

	if tags, err := storage.DockerRegistry.Tags(storage.RepoAddress); err != nil {
		return nil, fmt.Errorf("unable to fetch tags for repo %q: %s", storage.RepoAddress, err)
	} else {
		logboek.Debug.LogF("-- RepoStagesStorage.GetRepoImagesBySignature fetched tags for %q: %#v\n", storage.RepoAddress, tags)
		for _, tag := range tags {
			if !strings.HasPrefix(tag, signature) {
				logboek.Debug.LogF("Discard tag %q: should have prefix %q\n", tag, signature)
				continue
			}
			if _, uniqueID, err := getSignatureAndUniqueIDFromRepoStageImageTag(tag); err != nil {
				if isUnexpectedTagFormatError(err) {
					logboek.Debug.LogLn(strings.Title(err.Error()))
					continue
				}
				return nil, err
			} else {
				logboek.Debug.LogF("Tag %q is suitable for signature %q\n", tag, signature)
				res = append(res, image.StageID{Signature: signature, UniqueID: uniqueID})
			}
		}
	}

	logboek.Debug.LogF("-- RepoStagesStorage.GetRepoImagesBySignature result for %q: %#v\n", storage.RepoAddress, res)

	return res, nil
}

func (storage *RepoStagesStorage) GetStageDescription(projectName, signature string, uniqueID int64) (*image.StageDescription, error) {
	stageImageName := storage.ConstructStageImageName(projectName, signature, uniqueID)

	logboek.Debug.LogF("-- RepoStagesStorage GetStageDescription %s %s %d\n", projectName, signature, uniqueID)
	logboek.Debug.LogF("-- RepoStagesStorage stageImageName = %q\n", stageImageName)

	if imgInfo, err := storage.DockerRegistry.TryGetRepoImage(stageImageName); err != nil {
		return nil, err
	} else if imgInfo != nil {
		return &image.StageDescription{
			StageID: &image.StageID{Signature: signature, UniqueID: uniqueID},
			Info:    imgInfo,
		}, nil
	}
	return nil, nil
}

func (storage *RepoStagesStorage) AddManagedImage(projectName, imageName string) error {
	logboek.Debug.LogF("-- RepoStagesStorage.AddManagedImage %s %s\n", projectName, imageName)

	fullImageName := makeRepoManagedImageRecord(storage.RepoAddress, imageName)

	if _, lock, err := werf.AcquireHostLock(fmt.Sprintf("managed_image.%s-%s", projectName, imageName), lockgate.AcquireOptions{}); err != nil {
		return err
	} else {
		defer werf.ReleaseHostLock(lock)
	}

	if isExists, err := storage.DockerRegistry.IsRepoImageExists(fullImageName); err != nil {
		return err
	} else if isExists {
		logboek.Debug.LogF("-- RepoStagesStorage.AddManagedImage record %q is exists => exiting\n", fullImageName)
		return nil
	}

	logboek.Debug.LogF("-- RepoStagesStorage.AddManagedImage record %q does not exist => creating record\n", fullImageName)

	switch storage.ContainerRuntime.(type) {
	case *container_runtime.LocalDockerServerRuntime:
		if err := docker.CreateImage(fullImageName); err != nil {
			return fmt.Errorf("unable to create image %q: %s", fullImageName, err)
		}
		defer func() {
			if err := docker.CliRmi("--force", fullImageName); err != nil {
				// TODO: errored repo state
				logboek.Error.LogF("unable to remove temporary image %q: %s", fullImageName, err)
			}
		}()

		if err := docker.CliPushWithRetries(fullImageName); err != nil {
			return fmt.Errorf("unable to push image %q: %s", fullImageName, err)
		}

		return nil
	default: // TODO: case *container_runtime.LocalHostRuntime:
		panic("not implemented")
	}
}

func (storage *RepoStagesStorage) RmManagedImage(projectName, imageName string) error {
	logboek.Debug.LogF("-- RepoStagesStorage.RmManagedImage %s %s\n", projectName, imageName)

	fullImageName := makeRepoManagedImageRecord(storage.RepoAddress, imageName)

	if imgInfo, err := storage.DockerRegistry.TryGetRepoImage(fullImageName); err != nil {
		return fmt.Errorf("unable to get repo image %q info: %s", fullImageName, err)
	} else if imgInfo == nil {
		logboek.Debug.LogF("-- RepoStagesStorage.RmManagedImage record %q does not exist => exiting\n", fullImageName)
		return nil
	} else {
		if err := storage.DockerRegistry.DeleteRepoImage(imgInfo); err != nil {
			return fmt.Errorf("unable to delete image %q from repo: %s", fullImageName, err)
		}
	}

	return nil
}

func (storage *RepoStagesStorage) GetManagedImages(projectName string) ([]string, error) {
	logboek.Debug.LogF("-- RepoStagesStorage.GetManagedImages %s\n", projectName)

	var res []string

	if tags, err := storage.DockerRegistry.Tags(storage.RepoAddress); err != nil {
		return nil, fmt.Errorf("unable to get repo %q tags: %s", storage.RepoAddress, err)
	} else {
		for _, tag := range tags {
			if !strings.HasPrefix(tag, RepoManagedImageRecord_ImageTagPrefix) {
				continue
			}

			tag = strings.ReplaceAll(tag, "__slash__", "/")
			tag = strings.ReplaceAll(tag, "__plus__", "+")

			managedImageName := strings.TrimPrefix(tag, RepoManagedImageRecord_ImageTagPrefix)
			if managedImageName == NamelessImageRecordTag {
				res = append(res, "")
			} else {
				res = append(res, managedImageName)
			}
		}
	}

	return res, nil
}

func (storage *RepoStagesStorage) FetchImage(img container_runtime.Image) error {
	switch containerRuntime := storage.ContainerRuntime.(type) {
	case *container_runtime.LocalDockerServerRuntime:
		return containerRuntime.PullImageFromRegistry(img)
	default:
		// TODO: case *container_runtime.LocalHostRuntime:
		panic("not implemented")
	}
}

func (storage *RepoStagesStorage) StoreImage(img container_runtime.Image) error {
	switch containerRuntime := storage.ContainerRuntime.(type) {
	case *container_runtime.LocalDockerServerRuntime:
		dockerImage := img.(*container_runtime.DockerImage)

		if dockerImage.Image.GetBuiltId() != "" {
			return containerRuntime.PushBuiltImage(img)
		} else {
			return containerRuntime.PushImage(img)
		}

	default:
		// TODO: case *container_runtime.LocalHostRuntime:
		panic("not implemented")
	}
}

func (storage *RepoStagesStorage) ShouldFetchImage(img container_runtime.Image) (bool, error) {
	switch storage.ContainerRuntime.(type) {
	case *container_runtime.LocalDockerServerRuntime:
		dockerImage := img.(*container_runtime.DockerImage)
		return !dockerImage.Image.IsExistsLocally(), nil
	default:
		panic("not implemented")
	}
}

func (storage *RepoStagesStorage) String() string {
	return fmt.Sprintf("repo stages storage (%q)", storage.RepoAddress)
}

func (storage *RepoStagesStorage) Address() string {
	return storage.RepoAddress
}

func makeRepoManagedImageRecord(repoAddress, imageName string) string {
	tagSuffix := imageName
	if imageName == "" {
		tagSuffix = NamelessImageRecordTag
	}

	tagSuffix = strings.ReplaceAll(tagSuffix, "/", "__slash__")
	tagSuffix = strings.ReplaceAll(tagSuffix, "+", "__plus__")

	return fmt.Sprintf(RepoManagedImageRecord_ImageNameFormat, repoAddress, tagSuffix)
}
<|endoftext|>