<commit_before>// (C) Copyright 2021 Hewlett Packard Enterprise Development LP
//
// 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 oneview

import (
	"strings"

	"github.com/HewlettPackard/oneview-golang/ov"
	"github.com/HewlettPackard/oneview-golang/utils"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

func resourceFirmwareDrivers() *schema.Resource {
	return &schema.Resource{
		Read:   resourceFirmwareDriversRead,
		Create: resourceFirmwareDriversCreate,
		Delete: resourceFirmwareDriversDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"category": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"created": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"modified": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"description": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"etag": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"name": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"state": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"status": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"type": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"baseline_short_name": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"bundle_size": {
				Type:     schema.TypeInt,
				Computed: true,
			},
			"bundle_type": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"esxi_os_driver_meta_data": {
				Computed: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
			"fw_components": {
				Computed: true,
				Type:     schema.TypeSet,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"component_version": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"file_name": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"name": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"sw_key_name_list": {
							Computed: true,
							Type:     schema.TypeSet,
							Elem: &schema.Schema{
								Type: schema.TypeString,
							},
							Set: schema.HashString,
						},
					},
				},
			},
			"hotfixes": {
				Computed: true,
				Type:     schema.TypeSet,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"hotfix_name": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"release_data": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"resource_id": {
							Type:     schema.TypeString,
							Computed: true,
						},
					},
				},
			},
			"hpsum_version": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"iso_file_name": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"last_task_uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"mirror_list": {
				Computed: true,
				Type:     schema.TypeMap,
				Elem: &schema.Schema{
					Type: schema.TypeList,
				},
			},
			"locations": {
				Computed: true,
				Type:     schema.TypeMap,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},
			"parent_bundle": {
				Computed: true,
				Type:     schema.TypeSet,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"parent_bundle_name": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"release_data": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"version": {
							Type:     schema.TypeString,
							Computed: true,
						},
					},
				},
			},
			"release_data": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"resource_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"resource_state": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"scope_uri": {
				Computed: true,
				Type:     schema.TypeString,
			},
			"signature_file_name": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"signature_file_required": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"supported_languages": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"supported_os_list": {
				Computed: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
			"sw_packages_full_path": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"uuid": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"version": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"xml_key_name": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"baseline_uri": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"hotfix_uris": {
				Type: schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set:      schema.HashString,
				Required: true,
				ForceNew: true,
			},
			"custom_baseline_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"initial_scope_uris": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set:      schema.HashString,
				ForceNew: true,
			},
			"force": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
		},
	}
}

func resourceFirmwareDriversCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	customBundle := ov.CustomServicePack{
		CustomBaselineName: d.Get("custom_baseline_name").(string),
		BaselineUri:        d.Get("baseline_uri").(string),
	}
	force := "false"
	if _, ok := d.GetOk("force"); ok {
		force = d.Get("force").(string)
	}
	if val, ok := d.GetOk("initial_scope_uris"); ok {
		rawInitialScopeUris := val.(*schema.Set).List()
		initialScopeUris := make([]utils.Nstring, len(rawInitialScopeUris))
		for i, raw := range rawInitialScopeUris {
			initialScopeUris[i] = utils.Nstring(raw.(string))
		}
		customBundle.InitialScopeUris = initialScopeUris
	}

	rawHotflixURI := d.Get("hotfix_uris").(*schema.Set).List()
	HotflixURI := make([]utils.Nstring, len(rawHotflixURI))
	for i, raw := range rawHotflixURI {
		HotflixURI[i] = utils.Nstring(raw.(string))
	}
	customBundle.HotfixUris = HotflixURI

	err := config.ovClient.CreateCustomServicePack(customBundle, force)
	if err != nil {
		return err
	}

	return resourceFirmwareDriversRead(d, meta)
}

func resourceFirmwareDriversRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	firmwareAll, err := config.ovClient.GetFirmwareBaselineList("", "", "")
	if err != nil || firmwareAll.Uri.IsNil() {
		d.SetId("")
		return nil
	}
	firmware := ov.FirmwareDrivers{}
	for i := range firmwareAll.Members {
		if firmwareAll.Members[i].Name != d.Get("custom_baseline_name").(string) {
			continue
		} else {
			firmware = firmwareAll.Members[i]
			d.Set("name", firmware.Name)
			d.Set("type", firmware.Type)
			d.Set("created", firmware.Created)
			d.Set("modified", firmware.Modified)
			d.Set("uri", firmware.Uri.String())
			d.Set("status", firmware.Status)
			d.Set("category", firmware.Category)
			d.Set("state", firmware.State)
			d.Set("etag", firmware.ETAG)
			d.Set("description", firmware.Description)
			d.Set("baseline_short_name", firmware.BaselineShortName)
			d.Set("bundle_size", firmware.BundleSize)
			d.Set("bundle_type", firmware.BundleType)
			d.Set("esxi_os_driver_meta_data", firmware.EsxiOsDriverMetaData)
			d.Set("hpsum_version", firmware.HpsumVersion)
			d.Set("iso_file_name", firmware.IsoFileName)
			d.Set("last_task_uri", firmware.LastTaskUri)
			d.Set("release_data", firmware.ReleaseDate)
			d.Set("resource_id", firmware.ResourceId)
			d.Set("resource_state", firmware.ResourceState)
			d.Set("scope_uri", firmware.ScopesUri)
			d.Set("signature_file_name", firmware.SignatureFileName)
			d.Set("signature_file_required", firmware.SignatureFileRequired)
			d.Set("supported_languages", firmware.SupportedLanguages)
			d.Set("supported_os_list", firmware.SupportedOSList)
			d.Set("sw_packages_full_path", firmware.SwPackagesFullPath)
			d.Set("uuid", firmware.Uuid)
			d.Set("version", firmware.Version)
			d.Set("xml_key_name", firmware.XmlKeyName)

			fwcomponent := make([]map[string]interface{}, 0, len(firmware.FwComponents))
			for _, component := range firmware.FwComponents {
				fwcomponent = append(fwcomponent, map[string]interface{}{
					"component_version": component.ComponentVersion,
					"file_name":         component.FileName,
					"name":              component.Name,
					"sw_key_name_list":  component.SwKeyNameList,
				})
			}
			d.Set("fw_components", fwcomponent)

			hotFixes := make([]map[string]interface{}, 0, len(firmware.Hotfixes))
			for _, hotfix := range firmware.Hotfixes {
				hotFixes = append(hotFixes, map[string]interface{}{
					"hotfix_name":  hotfix.HotfixName,
					"release_data": hotfix.ReleaseDate,
					"resource_id":  hotfix.ResourceId,
				})
			}
			d.Set("hotfixes", hotFixes)

			parentBundle := make([]map[string]interface{}, 0, 1)
			parentBundle = append(parentBundle, map[string]interface{}{
				"parent_bundle_name": firmware.ParentBundle.ParentBundleName,
				"release_data":       firmware.ParentBundle.ReleaseDate,
				"version":            firmware.ParentBundle.Version,
			})

			d.Set("parent_bundle", parentBundle)

			d.Set("locations", firmware.Locations)
			d.Set("mirror_list", firmware.Mirrorlist)
			id := strings.Split(firmware.Uri.String(), "/")[3]

			d.SetId(id)
			return nil

		}
	}
	d.SetId("")
	return nil
}

func resourceFirmwareDriversDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	err := config.ovClient.DeleteFirmwareBaseline(d.Get("id").(string), "false")
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>delete_resource<commit_after>// (C) Copyright 2021 Hewlett Packard Enterprise Development LP
//
// 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 oneview

import (
	"strings"

	"github.com/HewlettPackard/oneview-golang/ov"
	"github.com/HewlettPackard/oneview-golang/utils"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

func resourceFirmwareDrivers() *schema.Resource {
	return &schema.Resource{
		Read:   resourceFirmwareDriversRead,
		Create: resourceFirmwareDriversCreate,
		Delete: resourceFirmwareDriversDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"category": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"created": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"modified": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"description": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"etag": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"name": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"state": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"status": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"type": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"baseline_short_name": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"bundle_size": {
				Type:     schema.TypeInt,
				Computed: true,
			},
			"bundle_type": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"esxi_os_driver_meta_data": {
				Computed: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
			"fw_components": {
				Computed: true,
				Type:     schema.TypeSet,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"component_version": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"file_name": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"name": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"sw_key_name_list": {
							Computed: true,
							Type:     schema.TypeSet,
							Elem: &schema.Schema{
								Type: schema.TypeString,
							},
							Set: schema.HashString,
						},
					},
				},
			},
			"hotfixes": {
				Computed: true,
				Type:     schema.TypeSet,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"hotfix_name": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"release_data": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"resource_id": {
							Type:     schema.TypeString,
							Computed: true,
						},
					},
				},
			},
			"hpsum_version": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"iso_file_name": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"last_task_uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"mirror_list": {
				Computed: true,
				Type:     schema.TypeMap,
				Elem: &schema.Schema{
					Type: schema.TypeList,
				},
			},
			"locations": {
				Computed: true,
				Type:     schema.TypeMap,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},
			"parent_bundle": {
				Computed: true,
				Type:     schema.TypeSet,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"parent_bundle_name": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"release_data": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"version": {
							Type:     schema.TypeString,
							Computed: true,
						},
					},
				},
			},
			"release_data": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"resource_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"resource_state": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"scope_uri": {
				Computed: true,
				Type:     schema.TypeString,
			},
			"signature_file_name": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"signature_file_required": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"supported_languages": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"supported_os_list": {
				Computed: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
			"sw_packages_full_path": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"uuid": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"version": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"xml_key_name": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"baseline_uri": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"hotfix_uris": {
				Type: schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set:      schema.HashString,
				Required: true,
				ForceNew: true,
			},
			"custom_baseline_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"initial_scope_uris": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set:      schema.HashString,
				ForceNew: true,
			},
			"force": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
		},
	}
}

func resourceFirmwareDriversCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	customBundle := ov.CustomServicePack{
		CustomBaselineName: d.Get("custom_baseline_name").(string),
		BaselineUri:        d.Get("baseline_uri").(string),
	}
	force := "false"
	if _, ok := d.GetOk("force"); ok {
		force = d.Get("force").(string)
	}
	if val, ok := d.GetOk("initial_scope_uris"); ok {
		rawInitialScopeUris := val.(*schema.Set).List()
		initialScopeUris := make([]utils.Nstring, len(rawInitialScopeUris))
		for i, raw := range rawInitialScopeUris {
			initialScopeUris[i] = utils.Nstring(raw.(string))
		}
		customBundle.InitialScopeUris = initialScopeUris
	}

	rawHotflixURI := d.Get("hotfix_uris").(*schema.Set).List()
	HotflixURI := make([]utils.Nstring, len(rawHotflixURI))
	for i, raw := range rawHotflixURI {
		HotflixURI[i] = utils.Nstring(raw.(string))
	}
	customBundle.HotfixUris = HotflixURI

	err := config.ovClient.CreateCustomServicePack(customBundle, force)
	if err != nil {
		return err
	}

	return resourceFirmwareDriversRead(d, meta)
}

func resourceFirmwareDriversRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	firmwareAll, err := config.ovClient.GetFirmwareBaselineList("", "", "")
	if err != nil || firmwareAll.Uri.IsNil() {
		d.SetId("")
		return nil
	}
	firmware := ov.FirmwareDrivers{}
	for i := range firmwareAll.Members {
		if firmwareAll.Members[i].Name != d.Get("custom_baseline_name").(string) {
			continue
		} else {
			firmware = firmwareAll.Members[i]
			d.Set("name", firmware.Name)
			d.Set("type", firmware.Type)
			d.Set("created", firmware.Created)
			d.Set("modified", firmware.Modified)
			d.Set("uri", firmware.Uri.String())
			d.Set("status", firmware.Status)
			d.Set("category", firmware.Category)
			d.Set("state", firmware.State)
			d.Set("etag", firmware.ETAG)
			d.Set("description", firmware.Description)
			d.Set("baseline_short_name", firmware.BaselineShortName)
			d.Set("bundle_size", firmware.BundleSize)
			d.Set("bundle_type", firmware.BundleType)
			d.Set("esxi_os_driver_meta_data", firmware.EsxiOsDriverMetaData)
			d.Set("hpsum_version", firmware.HpsumVersion)
			d.Set("iso_file_name", firmware.IsoFileName)
			d.Set("last_task_uri", firmware.LastTaskUri)
			d.Set("release_data", firmware.ReleaseDate)
			d.Set("resource_id", firmware.ResourceId)
			d.Set("resource_state", firmware.ResourceState)
			d.Set("scope_uri", firmware.ScopesUri)
			d.Set("signature_file_name", firmware.SignatureFileName)
			d.Set("signature_file_required", firmware.SignatureFileRequired)
			d.Set("supported_languages", firmware.SupportedLanguages)
			d.Set("supported_os_list", firmware.SupportedOSList)
			d.Set("sw_packages_full_path", firmware.SwPackagesFullPath)
			d.Set("uuid", firmware.Uuid)
			d.Set("version", firmware.Version)
			d.Set("xml_key_name", firmware.XmlKeyName)

			fwcomponent := make([]map[string]interface{}, 0, len(firmware.FwComponents))
			for _, component := range firmware.FwComponents {
				fwcomponent = append(fwcomponent, map[string]interface{}{
					"component_version": component.ComponentVersion,
					"file_name":         component.FileName,
					"name":              component.Name,
					"sw_key_name_list":  component.SwKeyNameList,
				})
			}
			d.Set("fw_components", fwcomponent)

			hotFixes := make([]map[string]interface{}, 0, len(firmware.Hotfixes))
			for _, hotfix := range firmware.Hotfixes {
				hotFixes = append(hotFixes, map[string]interface{}{
					"hotfix_name":  hotfix.HotfixName,
					"release_data": hotfix.ReleaseDate,
					"resource_id":  hotfix.ResourceId,
				})
			}
			d.Set("hotfixes", hotFixes)

			parentBundle := make([]map[string]interface{}, 0, 1)
			parentBundle = append(parentBundle, map[string]interface{}{
				"parent_bundle_name": firmware.ParentBundle.ParentBundleName,
				"release_data":       firmware.ParentBundle.ReleaseDate,
				"version":            firmware.ParentBundle.Version,
			})

			d.Set("parent_bundle", parentBundle)

			d.Set("locations", firmware.Locations)
			d.Set("mirror_list", firmware.Mirrorlist)
			id := strings.Split(firmware.Uri.String(), "/")[3]

			d.SetId(id)
			return nil

		}
	}
	d.SetId("")
	return nil
}

func resourceFirmwareDriversDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	err := config.ovClient.DeleteFirmwareBaseline(d.Id(), "false")
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package app

import (
	"flag"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"testing"

	"github.com/weaveworks/scope/render"
	"github.com/weaveworks/scope/report"
	"github.com/weaveworks/scope/test/fixture"
)

var (
	benchReportPath = flag.String("bench-report-path", "", "report file, or dir with files, to use for benchmarking (relative to this package)")
)

func readReportFiles(b *testing.B, path string) []report.Report {
	reports := []report.Report{}
	if err := filepath.Walk(path,
		func(p string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if info.IsDir() {
				return nil
			}
			rpt, err := report.MakeFromFile(p)
			if err != nil {
				return err
			}
			reports = append(reports, rpt)
			return nil
		}); err != nil {
		b.Fatal(err)
	}
	return reports
}

func BenchmarkReportUnmarshal(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		readReportFiles(b, *benchReportPath)
	}
}

func upgradeReports(reports []report.Report) []report.Report {
	upgraded := make([]report.Report, len(reports))
	for i, r := range reports {
		upgraded[i] = r.Upgrade()
	}
	return upgraded
}

func BenchmarkReportUpgrade(b *testing.B) {
	reports := readReportFiles(b, *benchReportPath)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		upgradeReports(reports)
	}
}

func BenchmarkReportMerge(b *testing.B) {
	reports := upgradeReports(readReportFiles(b, *benchReportPath))
	merger := NewSmartMerger()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		merger.Merge(reports)
	}
}

func getReport(b *testing.B) report.Report {
	r := fixture.Report
	if *benchReportPath != "" {
		r = NewSmartMerger().Merge(upgradeReports(readReportFiles(b, *benchReportPath)))
	}
	return r
}

func benchmarkRender(b *testing.B, f func(report.Report)) {
	r := getReport(b)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		b.StopTimer()
		render.ResetCache()
		b.StartTimer()
		f(r)
	}
}

func benchmarkRenderTopology(b *testing.B, topologyID string) {
	benchmarkRender(b, func(report report.Report) {
		renderer, filter, err := topologyRegistry.RendererForTopology(topologyID, url.Values{}, report)
		if err != nil {
			b.Fatal(err)
		}
		render.Render(report, renderer, filter)
	})
}

func BenchmarkRenderList(b *testing.B) {
	benchmarkRender(b, func(report report.Report) {
		topologyRegistry.renderTopologies(report, &http.Request{Form: url.Values{}})
	})
}

func BenchmarkRenderHosts(b *testing.B) {
	benchmarkRenderTopology(b, "hosts")
}

func BenchmarkRenderControllers(b *testing.B) {
	benchmarkRenderTopology(b, "kube-controllers")
}

func BenchmarkRenderPods(b *testing.B) {
	benchmarkRenderTopology(b, "pods")
}

func BenchmarkRenderContainers(b *testing.B) {
	benchmarkRenderTopology(b, "containers")
}

func BenchmarkRenderProcesses(b *testing.B) {
	benchmarkRenderTopology(b, "processes")
}
<commit_msg>refactor: extract helper for rendering a topology<commit_after>package app

import (
	"flag"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"testing"

	"github.com/weaveworks/scope/render"
	"github.com/weaveworks/scope/report"
	"github.com/weaveworks/scope/test/fixture"
)

var (
	benchReportPath = flag.String("bench-report-path", "", "report file, or dir with files, to use for benchmarking (relative to this package)")
)

func readReportFiles(b *testing.B, path string) []report.Report {
	reports := []report.Report{}
	if err := filepath.Walk(path,
		func(p string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if info.IsDir() {
				return nil
			}
			rpt, err := report.MakeFromFile(p)
			if err != nil {
				return err
			}
			reports = append(reports, rpt)
			return nil
		}); err != nil {
		b.Fatal(err)
	}
	return reports
}

func BenchmarkReportUnmarshal(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		readReportFiles(b, *benchReportPath)
	}
}

func upgradeReports(reports []report.Report) []report.Report {
	upgraded := make([]report.Report, len(reports))
	for i, r := range reports {
		upgraded[i] = r.Upgrade()
	}
	return upgraded
}

func BenchmarkReportUpgrade(b *testing.B) {
	reports := readReportFiles(b, *benchReportPath)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		upgradeReports(reports)
	}
}

func BenchmarkReportMerge(b *testing.B) {
	reports := upgradeReports(readReportFiles(b, *benchReportPath))
	merger := NewSmartMerger()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		merger.Merge(reports)
	}
}

func getReport(b *testing.B) report.Report {
	r := fixture.Report
	if *benchReportPath != "" {
		r = NewSmartMerger().Merge(upgradeReports(readReportFiles(b, *benchReportPath)))
	}
	return r
}

func benchmarkRender(b *testing.B, f func(report.Report)) {
	r := getReport(b)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		b.StopTimer()
		render.ResetCache()
		b.StartTimer()
		f(r)
	}
}

func renderForTopology(b *testing.B, topologyID string, report report.Report) report.Nodes {
	renderer, filter, err := topologyRegistry.RendererForTopology(topologyID, url.Values{}, report)
	if err != nil {
		b.Fatal(err)
	}
	return render.Render(report, renderer, filter).Nodes
}

func benchmarkRenderTopology(b *testing.B, topologyID string) {
	benchmarkRender(b, func(report report.Report) {
		renderForTopology(b, topologyID, report)
	})
}

func BenchmarkRenderList(b *testing.B) {
	benchmarkRender(b, func(report report.Report) {
		topologyRegistry.renderTopologies(report, &http.Request{Form: url.Values{}})
	})
}

func BenchmarkRenderHosts(b *testing.B) {
	benchmarkRenderTopology(b, "hosts")
}

func BenchmarkRenderControllers(b *testing.B) {
	benchmarkRenderTopology(b, "kube-controllers")
}

func BenchmarkRenderPods(b *testing.B) {
	benchmarkRenderTopology(b, "pods")
}

func BenchmarkRenderContainers(b *testing.B) {
	benchmarkRenderTopology(b, "containers")
}

func BenchmarkRenderProcesses(b *testing.B) {
	benchmarkRenderTopology(b, "processes")
}
<|endoftext|>


<commit_before>package s3api

import (
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/gorilla/mux"

	"github.com/chrislusf/seaweedfs/weed/util"
)

func (s3a *S3ApiServer) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {

	vars := mux.Vars(r)
	dstBucket := vars["bucket"]
	dstObject := getObject(vars)

	// Copy source path.
	cpSrcPath, err := url.QueryUnescape(r.Header.Get("X-Amz-Copy-Source"))
	if err != nil {
		// Save unescaped string as is.
		cpSrcPath = r.Header.Get("X-Amz-Copy-Source")
	}

	srcBucket, srcObject := pathToBucketAndObject(cpSrcPath)
	// If source object is empty or bucket is empty, reply back invalid copy source.
	if srcObject == "" || srcBucket == "" {
		writeErrorResponse(w, ErrInvalidCopySource, r.URL)
		return
	}

	if srcBucket == dstBucket && srcObject == dstObject {
		writeErrorResponse(w, ErrInvalidCopySource, r.URL)
		return
	}

	dstUrl := fmt.Sprintf("http://%s%s/%s%s?collection=%s",
		s3a.option.Filer, s3a.option.BucketsPath, dstBucket, dstObject, dstBucket)
	srcUrl := fmt.Sprintf("http://%s%s/%s%s",
		s3a.option.Filer, s3a.option.BucketsPath, srcBucket, srcObject)

	_, _, dataReader, err := util.DownloadFile(srcUrl)
	if err != nil {
		writeErrorResponse(w, ErrInvalidCopySource, r.URL)
		return
	}
	defer dataReader.Close()

	etag, errCode := s3a.putToFiler(r, dstUrl, dataReader)

	if errCode != ErrNone {
		writeErrorResponse(w, errCode, r.URL)
		return
	}

	setEtag(w, etag)

	response := CopyObjectResult{
		ETag:         etag,
		LastModified: time.Now(),
	}

	writeSuccessResponseXML(w, encodeResponse(response))

}

func pathToBucketAndObject(path string) (bucket, object string) {
	path = strings.TrimPrefix(path, "/")
	parts := strings.SplitN(path, "/", 2)
	if len(parts) == 2 {
		return parts[0], "/" + parts[1]
	}
	return parts[0], "/"
}

type CopyPartResult struct {
	LastModified time.Time `xml:"LastModified"`
	ETag         string    `xml:"ETag"`
}

func (s3a *S3ApiServer) CopyObjectPartHandler(w http.ResponseWriter, r *http.Request) {
	// https://docs.aws.amazon.com/AmazonS3/latest/dev/CopyingObjctsUsingRESTMPUapi.html
	// https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPartCopy.html
	vars := mux.Vars(r)
	dstBucket := vars["bucket"]
	// dstObject := getObject(vars)

	// Copy source path.
	cpSrcPath, err := url.QueryUnescape(r.Header.Get("X-Amz-Copy-Source"))
	if err != nil {
		// Save unescaped string as is.
		cpSrcPath = r.Header.Get("X-Amz-Copy-Source")
	}

	srcBucket, srcObject := pathToBucketAndObject(cpSrcPath)
	// If source object is empty or bucket is empty, reply back invalid copy source.
	if srcObject == "" || srcBucket == "" {
		writeErrorResponse(w, ErrInvalidCopySource, r.URL)
		return
	}

	uploadID := r.URL.Query().Get("uploadId")
	partIDString := r.URL.Query().Get("partNumber")

	partID, err := strconv.Atoi(partIDString)
	if err != nil {
		writeErrorResponse(w, ErrInvalidPart, r.URL)
		return
	}

	// check partID with maximum part ID for multipart objects
	if partID > 10000 {
		writeErrorResponse(w, ErrInvalidMaxParts, r.URL)
		return
	}

	rangeHeader := r.Header.Get("x-amz-copy-source-range")

	dstUrl := fmt.Sprintf("http://%s%s/%s/%04d.part?collection=%s",
		s3a.option.Filer, s3a.genUploadsFolder(dstBucket), uploadID, partID-1, dstBucket)
	srcUrl := fmt.Sprintf("http://%s%s/%s%s",
		s3a.option.Filer, s3a.option.BucketsPath, srcBucket, srcObject)

	dataReader, err := util.ReadUrlAsReaderCloser(srcUrl, rangeHeader)
	if err != nil {
		writeErrorResponse(w, ErrInvalidCopySource, r.URL)
		return
	}
	defer dataReader.Close()

	etag, errCode := s3a.putToFiler(r, dstUrl, dataReader)

	if errCode != ErrNone {
		writeErrorResponse(w, errCode, r.URL)
		return
	}

	setEtag(w, etag)

	response := CopyPartResult{
		ETag:         etag,
		LastModified: time.Now(),
	}

	writeSuccessResponseXML(w, encodeResponse(response))

}
<commit_msg>refactoring<commit_after>package s3api

import (
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/gorilla/mux"

	"github.com/chrislusf/seaweedfs/weed/util"
)

func (s3a *S3ApiServer) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {

	vars := mux.Vars(r)
	dstBucket := vars["bucket"]
	dstObject := getObject(vars)

	// Copy source path.
	cpSrcPath, err := url.QueryUnescape(r.Header.Get("X-Amz-Copy-Source"))
	if err != nil {
		// Save unescaped string as is.
		cpSrcPath = r.Header.Get("X-Amz-Copy-Source")
	}

	srcBucket, srcObject := pathToBucketAndObject(cpSrcPath)
	// If source object is empty or bucket is empty, reply back invalid copy source.
	if srcObject == "" || srcBucket == "" {
		writeErrorResponse(w, ErrInvalidCopySource, r.URL)
		return
	}

	if srcBucket == dstBucket && srcObject == dstObject {
		writeErrorResponse(w, ErrInvalidCopySource, r.URL)
		return
	}

	dstUrl := fmt.Sprintf("http://%s%s/%s%s?collection=%s",
		s3a.option.Filer, s3a.option.BucketsPath, dstBucket, dstObject, dstBucket)
	srcUrl := fmt.Sprintf("http://%s%s/%s%s",
		s3a.option.Filer, s3a.option.BucketsPath, srcBucket, srcObject)

	_, _, dataReader, err := util.DownloadFile(srcUrl)
	if err != nil {
		writeErrorResponse(w, ErrInvalidCopySource, r.URL)
		return
	}
	defer dataReader.Close()

	etag, errCode := s3a.putToFiler(r, dstUrl, dataReader)

	if errCode != ErrNone {
		writeErrorResponse(w, errCode, r.URL)
		return
	}

	setEtag(w, etag)

	response := CopyObjectResult{
		ETag:         etag,
		LastModified: time.Now(),
	}

	writeSuccessResponseXML(w, encodeResponse(response))

}

func pathToBucketAndObject(path string) (bucket, object string) {
	path = strings.TrimPrefix(path, "/")
	parts := strings.SplitN(path, "/", 2)
	if len(parts) == 2 {
		return parts[0], "/" + parts[1]
	}
	return parts[0], "/"
}

type CopyPartResult struct {
	LastModified time.Time `xml:"LastModified"`
	ETag         string    `xml:"ETag"`
}

func (s3a *S3ApiServer) CopyObjectPartHandler(w http.ResponseWriter, r *http.Request) {
	// https://docs.aws.amazon.com/AmazonS3/latest/dev/CopyingObjctsUsingRESTMPUapi.html
	// https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPartCopy.html
	vars := mux.Vars(r)
	dstBucket := vars["bucket"]
	// dstObject := getObject(vars)

	// Copy source path.
	cpSrcPath, err := url.QueryUnescape(r.Header.Get("X-Amz-Copy-Source"))
	if err != nil {
		// Save unescaped string as is.
		cpSrcPath = r.Header.Get("X-Amz-Copy-Source")
	}

	srcBucket, srcObject := pathToBucketAndObject(cpSrcPath)
	// If source object is empty or bucket is empty, reply back invalid copy source.
	if srcObject == "" || srcBucket == "" {
		writeErrorResponse(w, ErrInvalidCopySource, r.URL)
		return
	}

	uploadID := r.URL.Query().Get("uploadId")
	partIDString := r.URL.Query().Get("partNumber")

	partID, err := strconv.Atoi(partIDString)
	if err != nil {
		writeErrorResponse(w, ErrInvalidPart, r.URL)
		return
	}

	// check partID with maximum part ID for multipart objects
	if partID > globalMaxPartID {
		writeErrorResponse(w, ErrInvalidMaxParts, r.URL)
		return
	}

	rangeHeader := r.Header.Get("x-amz-copy-source-range")

	dstUrl := fmt.Sprintf("http://%s%s/%s/%04d.part?collection=%s",
		s3a.option.Filer, s3a.genUploadsFolder(dstBucket), uploadID, partID-1, dstBucket)
	srcUrl := fmt.Sprintf("http://%s%s/%s%s",
		s3a.option.Filer, s3a.option.BucketsPath, srcBucket, srcObject)

	dataReader, err := util.ReadUrlAsReaderCloser(srcUrl, rangeHeader)
	if err != nil {
		writeErrorResponse(w, ErrInvalidCopySource, r.URL)
		return
	}
	defer dataReader.Close()

	etag, errCode := s3a.putToFiler(r, dstUrl, dataReader)

	if errCode != ErrNone {
		writeErrorResponse(w, errCode, r.URL)
		return
	}

	setEtag(w, etag)

	response := CopyPartResult{
		ETag:         etag,
		LastModified: time.Now(),
	}

	writeSuccessResponseXML(w, encodeResponse(response))

}
<|endoftext|>


<commit_before>package expect

import (
	"flag"
	"fmt"
	"os"
	"reflect"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/wsxiaoys/terminal/color"
)

var (
	loaded         = false
	loadLock       = new(sync.Mutex)
	showStdout     = flag.Bool("vv", false, "turn on stdout")
	matchFlag      = flag.String("m", "", "Regular expression selecting which tests to run")
	notMatchFlag   = flag.String("M", "", "Regular expression selecting which tests not to run")
	summaryPath    = flag.String("summary", "", "Path to write a summary file to")
	pattern        *regexp.Regexp
	notPattern     *regexp.Regexp
	runner         *Runner
	stdout         = os.Stdout
	silentOut      *os.File
	beforeEach     = make([]func(), 0, 2)
	endTestErr     = new(error)
	summaryPattern = regexp.MustCompile(`(?s)(\d+) passed.*?(\d+) failed`)
)

func init() {
	os.Setenv("GO_TEST", "true")
}

func Expectify(suite interface{}, t *testing.T) {
	loadLock.Lock()
	if !loaded {
		flag.Parse()
		if len(*matchFlag) != 0 {
			pattern = regexp.MustCompile("(?i)" + *matchFlag)
		}
		if len(*notMatchFlag) != 0 {
			notPattern = regexp.MustCompile("(?i)" + *notMatchFlag)
		}
		if *showStdout == true {
			silentOut = stdout
		}
		os.Stdout = silentOut
	}
	loadLock.Unlock()

	var name string
	var res *result
	defer func() {
		if err := recover(); err != nil {
			if err == endTestErr {
				finish(t)
				return
			}
			os.Stdout = stdout
			if res != nil {
				res.Report()
			}
			color.Printf("@R 💣  %-75s\n", name)
			panic(err)
		}
	}()

	tp := reflect.TypeOf(suite)
	sv := reflect.ValueOf(suite)
	count := tp.NumMethod()

	runner = &Runner{
		results: make([]*result, 0, 10),
	}

	each, _ := tp.MethodByName("Each")
	if each.Func.IsValid() && each.Type.NumIn() != 2 {
		each = reflect.Method{}
	}

	announced := false
	for i := 0; i < count; i++ {
		method := tp.Method(i)
		// this method is not exported
		if len(method.PkgPath) != 0 {
			continue
		}
		name = method.Name
		typeName := sv.Elem().Type().String()

		if method.Type.NumIn() != 1 {
			continue
		}

		if pattern != nil && !pattern.MatchString(name) && !pattern.MatchString(typeName) {
			continue
		}
		if notPattern != nil && (notPattern.MatchString(name) || notPattern.MatchString(typeName)) {
			continue
		}

		os.Stdout = stdout
		res = runner.Start(name, typeName)
		var f = func() {
			method.Func.Call([]reflect.Value{sv})
			if runner.End() == false || testing.Verbose() {
				if announced == false {
					color.Printf("\n@!%s@|\n", typeName)
					announced = true
				}
				res.Report()
			}
		}
		for i := 0; i < len(beforeEach); i++ {
			beforeEach[i]()
		}
		if each.Func.IsValid() {
			each.Func.Call([]reflect.Value{sv, reflect.ValueOf(f)})
		} else {
			f()
		}
		os.Stdout = silentOut
	}
	finish(t)
}

func finish(t *testing.T) {
	passed := 0
	for _, result := range runner.results {
		if result.Passed() {
			passed++
		}
	}
	failed := len(runner.results) - passed
	if failed != 0 {
		os.Stdout = stdout
		fmt.Println("\nFailure summary")
		for _, r := range runner.results {
			if r.Passed() == false {
				r.Summary()
			}
		}
		fmt.Println()
		os.Stdout = silentOut
		t.Fail()
	}
	if path := *summaryPath; len(path) != 0 {
		updatePersistedSummary(path, passed)
	}
}

func updatePersistedSummary(path string, passed int) {
	file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
	if err != nil {
		panic(err)
	}
	file.Write([]byte(strconv.Itoa(passed)))
	file.Write([]byte{'\n'})
	file.Close()
}

func BeforeEach(f func()) {
	beforeEach = append(beforeEach, f)
}

type Runner struct {
	results []*result
	current *result
}

func (r *Runner) Start(name string, typeName string) *result {
	r.current = &result{
		method:   name,
		typeName: typeName,
		start:    time.Now(),
		failures: make([]*Failure, 0, 3),
	}
	r.results = append(r.results, r.current)
	return r.current
}

func (r *Runner) End() bool {
	r.current.end = time.Now()
	passed := r.current.Passed()
	r.current = nil
	return passed
}

func (r *Runner) Skip(format string, args ...interface{}) {
	if r.current != nil {
		r.current.Skip(format, args...)
	}
}

func (r *Runner) Errorf(format string, args ...interface{}) {
	file := "???"
	line := 1
	ok := false
	for i := 3; i < 10; i++ {
		_, file, line, ok = runtime.Caller(i)
		if ok == false || strings.HasSuffix(file, "_test.go") {
			break
		}
	}

	if ok {
		if index := strings.LastIndex(file, "/"); index >= 0 {
			file = file[index+1:]
		} else if index = strings.LastIndex(file, "\\"); index >= 0 {
			file = file[index+1:]
		}
	}

	failure := &Failure{
		message:  fmt.Sprintf(format, args...),
		location: fmt.Sprintf("%s:%d", file, line),
	}
	r.current.failures = append(r.current.failures, failure)
}

func (r *Runner) ErrorMessage(format string, args ...interface{}) {
	if r.current != nil {
		r.current.ErrorMessage(format, args...)
	}
}

type result struct {
	method      string
	failures    []*Failure
	typeName    string
	start       time.Time
	end         time.Time
	skipMessage string
	skip        bool
}

type Failure struct {
	message  string
	location string
}

func (r *result) Skip(format string, args ...interface{}) {
	r.skip = true
	r.skipMessage = fmt.Sprintf(format, args...)
}

func (r *result) Passed() bool {
	return r.skip || len(r.failures) == 0
}

func (r *result) ErrorMessage(format string, args ...interface{}) {
	l := len(r.failures)
	if l > 0 {
		r.failures[l-1].message = fmt.Sprintf(format, args...)
	}
}

func (r *result) Report() {
	if r.end.IsZero() {
		r.end = time.Now()
	}
	info := fmt.Sprintf(" %-70s%dms", r.method, r.end.Sub(r.start).Nanoseconds()/1000000)
	if r.skip {
		color.Println(" @y⸚", info)
		color.Println("   @." + r.skipMessage)
	} else if r.Passed() {
		color.Println(" @g✓", info)
	} else {
		color.Println(" @r×", info)
		for _, failure := range r.failures {
			color.Printf("    @.%-40s%s\n", failure.location, failure.message)
		}
	}
}

func (r *result) Summary() {
	info := fmt.Sprintf(" %s.%-40s", r.typeName, r.method)
	if r.skip {
		color.Println(" @y⸚", info)
	} else if r.Passed() {
		color.Println(" @g✓", info)
	} else {
		color.Print(" @r×", info)
		color.Printf("%2d\n", len(r.failures))
	}
}
<commit_msg>helps if I set loaded<commit_after>package expect

import (
	"flag"
	"fmt"
	"os"
	"reflect"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/wsxiaoys/terminal/color"
)

var (
	loaded         = false
	loadLock       = new(sync.Mutex)
	showStdout     = flag.Bool("vv", false, "turn on stdout")
	matchFlag      = flag.String("m", "", "Regular expression selecting which tests to run")
	notMatchFlag   = flag.String("M", "", "Regular expression selecting which tests not to run")
	summaryPath    = flag.String("summary", "", "Path to write a summary file to")
	pattern        *regexp.Regexp
	notPattern     *regexp.Regexp
	runner         *Runner
	stdout         = os.Stdout
	silentOut      *os.File
	beforeEach     = make([]func(), 0, 2)
	endTestErr     = new(error)
	summaryPattern = regexp.MustCompile(`(?s)(\d+) passed.*?(\d+) failed`)
)

func init() {
	os.Setenv("GO_TEST", "true")
}

func Expectify(suite interface{}, t *testing.T) {
	loadLock.Lock()
	if !loaded {
		flag.Parse()
		if len(*matchFlag) != 0 {
			pattern = regexp.MustCompile("(?i)" + *matchFlag)
		}
		if len(*notMatchFlag) != 0 {
			notPattern = regexp.MustCompile("(?i)" + *notMatchFlag)
		}
		if *showStdout == true {
			silentOut = stdout
		}
		os.Stdout = silentOut
		loaded = true
	}
	loadLock.Unlock()

	var name string
	var res *result
	defer func() {
		if err := recover(); err != nil {
			if err == endTestErr {
				finish(t)
				return
			}
			os.Stdout = stdout
			if res != nil {
				res.Report()
			}
			color.Printf("@R 💣  %-75s\n", name)
			panic(err)
		}
	}()

	tp := reflect.TypeOf(suite)
	sv := reflect.ValueOf(suite)
	count := tp.NumMethod()

	runner = &Runner{
		results: make([]*result, 0, 10),
	}

	each, _ := tp.MethodByName("Each")
	if each.Func.IsValid() && each.Type.NumIn() != 2 {
		each = reflect.Method{}
	}

	announced := false
	for i := 0; i < count; i++ {
		method := tp.Method(i)
		// this method is not exported
		if len(method.PkgPath) != 0 {
			continue
		}
		name = method.Name
		typeName := sv.Elem().Type().String()

		if method.Type.NumIn() != 1 {
			continue
		}

		if pattern != nil && !pattern.MatchString(name) && !pattern.MatchString(typeName) {
			continue
		}
		if notPattern != nil && (notPattern.MatchString(name) || notPattern.MatchString(typeName)) {
			continue
		}

		os.Stdout = stdout
		res = runner.Start(name, typeName)
		var f = func() {
			method.Func.Call([]reflect.Value{sv})
			if runner.End() == false || testing.Verbose() {
				if announced == false {
					color.Printf("\n@!%s@|\n", typeName)
					announced = true
				}
				res.Report()
			}
		}
		for i := 0; i < len(beforeEach); i++ {
			beforeEach[i]()
		}
		if each.Func.IsValid() {
			each.Func.Call([]reflect.Value{sv, reflect.ValueOf(f)})
		} else {
			f()
		}
		os.Stdout = silentOut
	}
	finish(t)
}

func finish(t *testing.T) {
	passed := 0
	for _, result := range runner.results {
		if result.Passed() {
			passed++
		}
	}
	failed := len(runner.results) - passed
	if failed != 0 {
		os.Stdout = stdout
		fmt.Println("\nFailure summary")
		for _, r := range runner.results {
			if r.Passed() == false {
				r.Summary()
			}
		}
		fmt.Println()
		os.Stdout = silentOut
		t.Fail()
	}
	if path := *summaryPath; len(path) != 0 {
		updatePersistedSummary(path, passed)
	}
}

func updatePersistedSummary(path string, passed int) {
	file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
	if err != nil {
		panic(err)
	}
	file.Write([]byte(strconv.Itoa(passed)))
	file.Write([]byte{'\n'})
	file.Close()
}

func BeforeEach(f func()) {
	beforeEach = append(beforeEach, f)
}

type Runner struct {
	results []*result
	current *result
}

func (r *Runner) Start(name string, typeName string) *result {
	r.current = &result{
		method:   name,
		typeName: typeName,
		start:    time.Now(),
		failures: make([]*Failure, 0, 3),
	}
	r.results = append(r.results, r.current)
	return r.current
}

func (r *Runner) End() bool {
	r.current.end = time.Now()
	passed := r.current.Passed()
	r.current = nil
	return passed
}

func (r *Runner) Skip(format string, args ...interface{}) {
	if r.current != nil {
		r.current.Skip(format, args...)
	}
}

func (r *Runner) Errorf(format string, args ...interface{}) {
	file := "???"
	line := 1
	ok := false
	for i := 3; i < 10; i++ {
		_, file, line, ok = runtime.Caller(i)
		if ok == false || strings.HasSuffix(file, "_test.go") {
			break
		}
	}

	if ok {
		if index := strings.LastIndex(file, "/"); index >= 0 {
			file = file[index+1:]
		} else if index = strings.LastIndex(file, "\\"); index >= 0 {
			file = file[index+1:]
		}
	}

	failure := &Failure{
		message:  fmt.Sprintf(format, args...),
		location: fmt.Sprintf("%s:%d", file, line),
	}
	r.current.failures = append(r.current.failures, failure)
}

func (r *Runner) ErrorMessage(format string, args ...interface{}) {
	if r.current != nil {
		r.current.ErrorMessage(format, args...)
	}
}

type result struct {
	method      string
	failures    []*Failure
	typeName    string
	start       time.Time
	end         time.Time
	skipMessage string
	skip        bool
}

type Failure struct {
	message  string
	location string
}

func (r *result) Skip(format string, args ...interface{}) {
	r.skip = true
	r.skipMessage = fmt.Sprintf(format, args...)
}

func (r *result) Passed() bool {
	return r.skip || len(r.failures) == 0
}

func (r *result) ErrorMessage(format string, args ...interface{}) {
	l := len(r.failures)
	if l > 0 {
		r.failures[l-1].message = fmt.Sprintf(format, args...)
	}
}

func (r *result) Report() {
	if r.end.IsZero() {
		r.end = time.Now()
	}
	info := fmt.Sprintf(" %-70s%dms", r.method, r.end.Sub(r.start).Nanoseconds()/1000000)
	if r.skip {
		color.Println(" @y⸚", info)
		color.Println("   @." + r.skipMessage)
	} else if r.Passed() {
		color.Println(" @g✓", info)
	} else {
		color.Println(" @r×", info)
		for _, failure := range r.failures {
			color.Printf("    @.%-40s%s\n", failure.location, failure.message)
		}
	}
}

func (r *result) Summary() {
	info := fmt.Sprintf(" %s.%-40s", r.typeName, r.method)
	if r.skip {
		color.Println(" @y⸚", info)
	} else if r.Passed() {
		color.Println(" @g✓", info)
	} else {
		color.Print(" @r×", info)
		color.Printf("%2d\n", len(r.failures))
	}
}
<|endoftext|>


<commit_before>package gaurun

import (
	"bytes"
	"fmt"
	"net/http"
	"runtime"

	"github.com/BurntSushi/toml"
)

type ConfToml struct {
	Core    SectionCore    `toml:"core"`
	Api     SectionApi     `toml:"api"`
	Android SectionAndroid `toml:"android"`
	Ios     SectionIos     `toml:"ios"`
	Log     SectionLog     `toml:"log"`
}

type SectionCore struct {
	Port            string `toml:"port"`
	WorkerNum       int    `toml:"workers"`
	QueueNum        int    `toml:"queues"`
	NotificationMax int    `toml:"notification_max"`
}

type SectionApi struct {
	PushUri      string `toml:"push_uri"`
	StatGoUri    string `toml:"stat_go_uri"`
	StatAppUri   string `toml:"stat_app_uri"`
	ConfigAppUri string `toml:"config_app_uri"`
}

type SectionAndroid struct {
	Enabled          bool   `toml:"enabled"`
	ApiKey           string `toml:"apikey"`
	Timeout          int    `toml:"timeout"`
	KeepAliveTimeout int    `toml:"keepalive_timeout"`
	KeepAliveConns   int    `toml:"keepalive_conns"`
	RetryMax         int    `toml:"retry_max"`
}

type SectionIos struct {
	Enabled          bool   `toml:"enabled"`
	PemCertPath      string `toml:"pem_cert_path"`
	PemKeyPath       string `toml:"pem_key_path"`
	Sandbox          bool   `toml:"sandbox"`
	RetryMax         int    `toml:"retry_max"`
	Timeout          int    `toml:"timeout"`
	KeepAliveTimeout int    `toml:"keepalive_timeout"`
	KeepAliveConns   int    `toml:"keepalive_conns"`
	Topic            string `toml:"topic"`
}

type SectionLog struct {
	AccessLog string `toml:"access_log"`
	ErrorLog  string `toml:"error_log"`
	Level     string `toml:"level"`
}

func BuildDefaultConf() ConfToml {
	var conf ConfToml
	// Core
	conf.Core.Port = "1056"
	conf.Core.WorkerNum = runtime.NumCPU()
	conf.Core.QueueNum = 8192
	conf.Core.NotificationMax = 100
	// Api
	conf.Api.PushUri = "/push"
	conf.Api.StatGoUri = "/stat/go"
	conf.Api.StatAppUri = "/stat/app"
	conf.Api.ConfigAppUri = "/config/app"
	// Android
	conf.Android.ApiKey = ""
	conf.Android.Enabled = true
	conf.Android.Timeout = 5
	conf.Android.KeepAliveTimeout = 30
	conf.Android.KeepAliveConns = conf.Core.WorkerNum
	conf.Android.RetryMax = 1
	// iOS
	conf.Ios.Enabled = true
	conf.Ios.PemCertPath = ""
	conf.Ios.PemKeyPath = ""
	conf.Ios.Sandbox = true
	conf.Ios.RetryMax = 1
	conf.Ios.Timeout = 5
	conf.Ios.KeepAliveTimeout = 30
	conf.Ios.KeepAliveConns = conf.Core.WorkerNum
	conf.Ios.Topic = ""
	// log
	conf.Log.AccessLog = "stdout"
	conf.Log.ErrorLog = "stderr"
	conf.Log.Level = "error"
	return conf
}

func LoadConf(confGaurun ConfToml, confPath string) (ConfToml, error) {
	_, err := toml.DecodeFile(confPath, &confGaurun)
	if err != nil {
		return confGaurun, err
	}
	return confGaurun, nil
}

func ConfigHandler(w http.ResponseWriter, r *http.Request) {
	var b bytes.Buffer
	e := toml.NewEncoder(&b)
	result := ConfGaurun
	// hide Apikey
	result.Android.ApiKey = "..."
	err := e.Encode(result)
	if err != nil {
		msg := "Response-body could not be created"
		LogError.Error(msg)
		http.Error(w, msg, http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "text/plain")
	w.Header().Set("Server", serverHeader())
	fmt.Fprintf(w, b.String())
}
<commit_msg>style: specified assigned value with runtime.NumCPU().<commit_after>package gaurun

import (
	"bytes"
	"fmt"
	"net/http"
	"runtime"

	"github.com/BurntSushi/toml"
)

type ConfToml struct {
	Core    SectionCore    `toml:"core"`
	Api     SectionApi     `toml:"api"`
	Android SectionAndroid `toml:"android"`
	Ios     SectionIos     `toml:"ios"`
	Log     SectionLog     `toml:"log"`
}

type SectionCore struct {
	Port            string `toml:"port"`
	WorkerNum       int    `toml:"workers"`
	QueueNum        int    `toml:"queues"`
	NotificationMax int    `toml:"notification_max"`
}

type SectionApi struct {
	PushUri      string `toml:"push_uri"`
	StatGoUri    string `toml:"stat_go_uri"`
	StatAppUri   string `toml:"stat_app_uri"`
	ConfigAppUri string `toml:"config_app_uri"`
}

type SectionAndroid struct {
	Enabled          bool   `toml:"enabled"`
	ApiKey           string `toml:"apikey"`
	Timeout          int    `toml:"timeout"`
	KeepAliveTimeout int    `toml:"keepalive_timeout"`
	KeepAliveConns   int    `toml:"keepalive_conns"`
	RetryMax         int    `toml:"retry_max"`
}

type SectionIos struct {
	Enabled          bool   `toml:"enabled"`
	PemCertPath      string `toml:"pem_cert_path"`
	PemKeyPath       string `toml:"pem_key_path"`
	Sandbox          bool   `toml:"sandbox"`
	RetryMax         int    `toml:"retry_max"`
	Timeout          int    `toml:"timeout"`
	KeepAliveTimeout int    `toml:"keepalive_timeout"`
	KeepAliveConns   int    `toml:"keepalive_conns"`
	Topic            string `toml:"topic"`
}

type SectionLog struct {
	AccessLog string `toml:"access_log"`
	ErrorLog  string `toml:"error_log"`
	Level     string `toml:"level"`
}

func BuildDefaultConf() ConfToml {
	numCPU := runtime.NumCPU()

	var conf ConfToml
	// Core
	conf.Core.Port = "1056"
	conf.Core.WorkerNum = numCPU
	conf.Core.QueueNum = 8192
	conf.Core.NotificationMax = 100
	// Api
	conf.Api.PushUri = "/push"
	conf.Api.StatGoUri = "/stat/go"
	conf.Api.StatAppUri = "/stat/app"
	conf.Api.ConfigAppUri = "/config/app"
	// Android
	conf.Android.ApiKey = ""
	conf.Android.Enabled = true
	conf.Android.Timeout = 5
	conf.Android.KeepAliveTimeout = 30
	conf.Android.KeepAliveConns = numCPU
	conf.Android.RetryMax = 1
	// iOS
	conf.Ios.Enabled = true
	conf.Ios.PemCertPath = ""
	conf.Ios.PemKeyPath = ""
	conf.Ios.Sandbox = true
	conf.Ios.RetryMax = 1
	conf.Ios.Timeout = 5
	conf.Ios.KeepAliveTimeout = 30
	conf.Ios.KeepAliveConns = numCPU
	conf.Ios.Topic = ""
	// log
	conf.Log.AccessLog = "stdout"
	conf.Log.ErrorLog = "stderr"
	conf.Log.Level = "error"
	return conf
}

func LoadConf(confGaurun ConfToml, confPath string) (ConfToml, error) {
	_, err := toml.DecodeFile(confPath, &confGaurun)
	if err != nil {
		return confGaurun, err
	}
	return confGaurun, nil
}

func ConfigHandler(w http.ResponseWriter, r *http.Request) {
	var b bytes.Buffer
	e := toml.NewEncoder(&b)
	result := ConfGaurun
	// hide Apikey
	result.Android.ApiKey = "..."
	err := e.Encode(result)
	if err != nil {
		msg := "Response-body could not be created"
		LogError.Error(msg)
		http.Error(w, msg, http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "text/plain")
	w.Header().Set("Server", serverHeader())
	fmt.Fprintf(w, b.String())
}
<|endoftext|>


<commit_before>/*
Copyright 2018 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 provider

import (
	"context"
	"io/ioutil"
	"strings"

	"github.com/oracle/oci-go-sdk/common"
	"github.com/oracle/oci-go-sdk/dns"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	yaml "gopkg.in/yaml.v2"

	"github.com/kubernetes-incubator/external-dns/endpoint"
	"github.com/kubernetes-incubator/external-dns/plan"
)

const ociRecordTTL = 300

// OCIAuthConfig holds connection parameters for the OCI API.
type OCIAuthConfig struct {
	Region      string `yaml:"region"`
	TenancyID   string `yaml:"tenancy"`
	UserID      string `yaml:"user"`
	PrivateKey  string `yaml:"key"`
	Fingerprint string `yaml:"fingerprint"`
	Passphrase  string `yaml:"passphrase"`
}

// OCIConfig holds the configuration for the OCI Provider.
type OCIConfig struct {
	Auth          OCIAuthConfig `yaml:"auth"`
	CompartmentID string        `yaml:"compartment"`
}

// OCIProvider is an implementation of Provider for Oracle Cloud Infrastructure
// (OCI) DNS.
type OCIProvider struct {
	client ociDNSClient
	cfg    OCIConfig

	domainFilter DomainFilter
	zoneIDFilter ZoneIDFilter
	dryRun       bool
}

// ociDNSClient is the subset of the OCI DNS API required by the OCI Provider.
type ociDNSClient interface {
	ListZones(ctx context.Context, request dns.ListZonesRequest) (response dns.ListZonesResponse, err error)
	GetZoneRecords(ctx context.Context, request dns.GetZoneRecordsRequest) (response dns.GetZoneRecordsResponse, err error)
	PatchZoneRecords(ctx context.Context, request dns.PatchZoneRecordsRequest) (response dns.PatchZoneRecordsResponse, err error)
}

// LoadOCIConfig reads and parses the OCI ExternalDNS config file at the given
// path.
func LoadOCIConfig(path string) (*OCIConfig, error) {
	contents, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, errors.Wrapf(err, "reading OCI config file %q", path)
	}

	cfg := OCIConfig{}
	if err := yaml.Unmarshal(contents, &cfg); err != nil {
		return nil, errors.Wrapf(err, "parsing OCI config file %q", path)
	}
	return &cfg, nil
}

// NewOCIProvider initialises a new OCI DNS based Provider.
func NewOCIProvider(cfg OCIConfig, domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, dryRun bool) (*OCIProvider, error) {
	var client ociDNSClient
	client, err := dns.NewDnsClientWithConfigurationProvider(common.NewRawConfigurationProvider(
		cfg.Auth.TenancyID,
		cfg.Auth.UserID,
		cfg.Auth.Region,
		cfg.Auth.Fingerprint,
		cfg.Auth.PrivateKey,
		&cfg.Auth.Passphrase,
	))
	if err != nil {
		return nil, errors.Wrap(err, "initialising OCI DNS API client")
	}

	return &OCIProvider{
		client:       client,
		cfg:          cfg,
		domainFilter: domainFilter,
		zoneIDFilter: zoneIDFilter,
		dryRun:       dryRun,
	}, nil
}

func (p *OCIProvider) zones(ctx context.Context) (map[string]*dns.ZoneSummary, error) {
	zones := make(map[string]*dns.ZoneSummary)

	log.Debugf("Matching zones against domain filters: %v", p.domainFilter.filters)
	var page *string
	for {
		resp, err := p.client.ListZones(ctx, dns.ListZonesRequest{
			CompartmentId: &p.cfg.CompartmentID,
			ZoneType:      dns.ListZonesZoneTypePrimary,
			Page:          page,
		})
		if err != nil {
			return nil, errors.Wrapf(err, "listing zones in %q", p.cfg.CompartmentID)
		}

		for _, zone := range resp.Items {
			if p.domainFilter.Match(*zone.Name) && p.zoneIDFilter.Match(*zone.Id) {
				zones[*zone.Name] = &zone
				log.Debugf("Matched %q (%q)", *zone.Name, *zone.Id)
			} else {
				log.Debugf("Filtered %q (%q)", *zone.Name, *zone.Id)
			}
		}

		if page = resp.OpcNextPage; resp.OpcNextPage == nil {
			break
		}
	}

	if len(zones) == 0 {
		if p.domainFilter.IsConfigured() {
			log.Warnf("No zones in compartment %q match domain filters %v", p.cfg.CompartmentID, p.domainFilter.filters)
		} else {
			log.Warnf("No zones found in compartment %q", p.cfg.CompartmentID)
		}
	}

	return zones, nil
}

func (p *OCIProvider) newFilteredRecordOperations(endpoints []*endpoint.Endpoint, opType dns.RecordOperationOperationEnum) []dns.RecordOperation {
	ops := []dns.RecordOperation{}
	for _, endpoint := range endpoints {
		if p.domainFilter.Match(endpoint.DNSName) {
			ops = append(ops, newRecordOperation(endpoint, opType))
		}
	}
	return ops
}

// Records returns the list of records in a given hosted zone.
func (p *OCIProvider) Records() ([]*endpoint.Endpoint, error) {
	ctx := context.Background()
	zones, err := p.zones(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "getting zones")
	}

	endpoints := []*endpoint.Endpoint{}
	for _, zone := range zones {
		var page *string
		for {
			resp, err := p.client.GetZoneRecords(ctx, dns.GetZoneRecordsRequest{
				ZoneNameOrId:  zone.Id,
				Page:          page,
				CompartmentId: &p.cfg.CompartmentID,
			})
			if err != nil {
				return nil, errors.Wrapf(err, "getting records for zone %q", *zone.Id)
			}

			for _, record := range resp.Items {
				if !supportedRecordType(*record.Rtype) {
					continue
				}
				endpoints = append(endpoints,
					endpoint.NewEndpointWithTTL(
						*record.Domain,
						*record.Rtype,
						endpoint.TTL(*record.Ttl),
						*record.Rdata,
					),
				)
			}

			if page = resp.OpcNextPage; resp.OpcNextPage == nil {
				break
			}
		}
	}

	return endpoints, nil
}

// ApplyChanges applies a given set of changes to a given zone.
func (p *OCIProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
	log.Debugf("Processing chages: %+v", changes)

	ops := []dns.RecordOperation{}
	ops = append(ops, p.newFilteredRecordOperations(changes.Create, dns.RecordOperationOperationAdd)...)

	ops = append(ops, p.newFilteredRecordOperations(changes.UpdateNew, dns.RecordOperationOperationAdd)...)
	ops = append(ops, p.newFilteredRecordOperations(changes.UpdateOld, dns.RecordOperationOperationRemove)...)

	ops = append(ops, p.newFilteredRecordOperations(changes.Delete, dns.RecordOperationOperationRemove)...)

	if len(ops) == 0 {
		log.Info("All records are already up to date")
		return nil
	}

	zones, err := p.zones(ctx)
	if err != nil {
		return errors.Wrap(err, "fetching zones")
	}

	// Separate into per-zone change sets to be passed to OCI API.
	opsByZone := operationsByZone(zones, ops)
	for zoneID, ops := range opsByZone {
		log.Infof("Change zone: %q", zoneID)
		for _, op := range ops {
			log.Info(op)
		}
	}

	if p.dryRun {
		return nil
	}

	for zoneID, ops := range opsByZone {
		if _, err := p.client.PatchZoneRecords(ctx, dns.PatchZoneRecordsRequest{
			CompartmentId:           &p.cfg.CompartmentID,
			ZoneNameOrId:            &zoneID,
			PatchZoneRecordsDetails: dns.PatchZoneRecordsDetails{Items: ops},
		}); err != nil {
			return err
		}
	}

	return nil
}

// newRecordOperation returns a RecordOperation based on a given endpoint.
func newRecordOperation(ep *endpoint.Endpoint, opType dns.RecordOperationOperationEnum) dns.RecordOperation {
	targets := make([]string, len(ep.Targets))
	copy(targets, []string(ep.Targets))
	if ep.RecordType == endpoint.RecordTypeCNAME {
		targets[0] = ensureTrailingDot(targets[0])
	}
	rdata := strings.Join(targets, " ")

	ttl := ociRecordTTL
	if ep.RecordTTL.IsConfigured() {
		ttl = int(ep.RecordTTL)
	}

	return dns.RecordOperation{
		Domain:    &ep.DNSName,
		Rdata:     &rdata,
		Ttl:       &ttl,
		Rtype:     &ep.RecordType,
		Operation: opType,
	}
}

// operationsByZone segments a slice of RecordOperations by their zone.
func operationsByZone(zones map[string]*dns.ZoneSummary, ops []dns.RecordOperation) map[string][]dns.RecordOperation {
	changes := make(map[string][]dns.RecordOperation)

	zoneNameIDMapper := zoneIDName{}
	for _, z := range zones {
		zoneNameIDMapper.Add(*z.Id, *z.Name)
		changes[*z.Id] = []dns.RecordOperation{}
	}

	for _, op := range ops {
		if zoneID, _ := zoneNameIDMapper.FindZone(*op.Domain); zoneID != "" {
			changes[zoneID] = append(changes[zoneID], op)
		} else {
			log.Warnf("No matching zone for record operation %s", op)
		}
	}

	// Remove zones that don't have any changes.
	for zone, ops := range changes {
		if len(ops) == 0 {
			delete(changes, zone)
		}
	}

	return changes
}
<commit_msg>fixed an issue with handling multiple oci zones<commit_after>/*
Copyright 2018 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 provider

import (
	"context"
	"io/ioutil"
	"strings"

	"github.com/oracle/oci-go-sdk/common"
	"github.com/oracle/oci-go-sdk/dns"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	yaml "gopkg.in/yaml.v2"

	"github.com/kubernetes-incubator/external-dns/endpoint"
	"github.com/kubernetes-incubator/external-dns/plan"
)

const ociRecordTTL = 300

// OCIAuthConfig holds connection parameters for the OCI API.
type OCIAuthConfig struct {
	Region      string `yaml:"region"`
	TenancyID   string `yaml:"tenancy"`
	UserID      string `yaml:"user"`
	PrivateKey  string `yaml:"key"`
	Fingerprint string `yaml:"fingerprint"`
	Passphrase  string `yaml:"passphrase"`
}

// OCIConfig holds the configuration for the OCI Provider.
type OCIConfig struct {
	Auth          OCIAuthConfig `yaml:"auth"`
	CompartmentID string        `yaml:"compartment"`
}

// OCIProvider is an implementation of Provider for Oracle Cloud Infrastructure
// (OCI) DNS.
type OCIProvider struct {
	client ociDNSClient
	cfg    OCIConfig

	domainFilter DomainFilter
	zoneIDFilter ZoneIDFilter
	dryRun       bool
}

// ociDNSClient is the subset of the OCI DNS API required by the OCI Provider.
type ociDNSClient interface {
	ListZones(ctx context.Context, request dns.ListZonesRequest) (response dns.ListZonesResponse, err error)
	GetZoneRecords(ctx context.Context, request dns.GetZoneRecordsRequest) (response dns.GetZoneRecordsResponse, err error)
	PatchZoneRecords(ctx context.Context, request dns.PatchZoneRecordsRequest) (response dns.PatchZoneRecordsResponse, err error)
}

// LoadOCIConfig reads and parses the OCI ExternalDNS config file at the given
// path.
func LoadOCIConfig(path string) (*OCIConfig, error) {
	contents, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, errors.Wrapf(err, "reading OCI config file %q", path)
	}

	cfg := OCIConfig{}
	if err := yaml.Unmarshal(contents, &cfg); err != nil {
		return nil, errors.Wrapf(err, "parsing OCI config file %q", path)
	}
	return &cfg, nil
}

// NewOCIProvider initialises a new OCI DNS based Provider.
func NewOCIProvider(cfg OCIConfig, domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, dryRun bool) (*OCIProvider, error) {
	var client ociDNSClient
	client, err := dns.NewDnsClientWithConfigurationProvider(common.NewRawConfigurationProvider(
		cfg.Auth.TenancyID,
		cfg.Auth.UserID,
		cfg.Auth.Region,
		cfg.Auth.Fingerprint,
		cfg.Auth.PrivateKey,
		&cfg.Auth.Passphrase,
	))
	if err != nil {
		return nil, errors.Wrap(err, "initialising OCI DNS API client")
	}

	return &OCIProvider{
		client:       client,
		cfg:          cfg,
		domainFilter: domainFilter,
		zoneIDFilter: zoneIDFilter,
		dryRun:       dryRun,
	}, nil
}

func (p *OCIProvider) zones(ctx context.Context) (map[string]dns.ZoneSummary, error) {
	zones := make(map[string]dns.ZoneSummary)

	log.Debugf("Matching zones against domain filters: %v", p.domainFilter.filters)
	var page *string
	for {
		resp, err := p.client.ListZones(ctx, dns.ListZonesRequest{
			CompartmentId: &p.cfg.CompartmentID,
			ZoneType:      dns.ListZonesZoneTypePrimary,
			Page:          page,
		})
		if err != nil {
			return nil, errors.Wrapf(err, "listing zones in %q", p.cfg.CompartmentID)
		}

		for _, zone := range resp.Items {
			if p.domainFilter.Match(*zone.Name) && p.zoneIDFilter.Match(*zone.Id) {
				zones[*zone.Name] = zone
				log.Debugf("Matched %q (%q)", *zone.Name, *zone.Id)
			} else {
				log.Debugf("Filtered %q (%q)", *zone.Name, *zone.Id)
			}
		}

		if page = resp.OpcNextPage; resp.OpcNextPage == nil {
			break
		}
	}

	if len(zones) == 0 {
		if p.domainFilter.IsConfigured() {
			log.Warnf("No zones in compartment %q match domain filters %v", p.cfg.CompartmentID, p.domainFilter.filters)
		} else {
			log.Warnf("No zones found in compartment %q", p.cfg.CompartmentID)
		}
	}

	return zones, nil
}

func (p *OCIProvider) newFilteredRecordOperations(endpoints []*endpoint.Endpoint, opType dns.RecordOperationOperationEnum) []dns.RecordOperation {
	ops := []dns.RecordOperation{}
	for _, endpoint := range endpoints {
		if p.domainFilter.Match(endpoint.DNSName) {
			ops = append(ops, newRecordOperation(endpoint, opType))
		}
	}
	return ops
}

// Records returns the list of records in a given hosted zone.
func (p *OCIProvider) Records() ([]*endpoint.Endpoint, error) {
	ctx := context.Background()
	zones, err := p.zones(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "getting zones")
	}

	endpoints := []*endpoint.Endpoint{}
	for _, zone := range zones {
		var page *string
		for {
			resp, err := p.client.GetZoneRecords(ctx, dns.GetZoneRecordsRequest{
				ZoneNameOrId:  zone.Id,
				Page:          page,
				CompartmentId: &p.cfg.CompartmentID,
			})
			if err != nil {
				return nil, errors.Wrapf(err, "getting records for zone %q", *zone.Id)
			}

			for _, record := range resp.Items {
				if !supportedRecordType(*record.Rtype) {
					continue
				}
				endpoints = append(endpoints,
					endpoint.NewEndpointWithTTL(
						*record.Domain,
						*record.Rtype,
						endpoint.TTL(*record.Ttl),
						*record.Rdata,
					),
				)
			}

			if page = resp.OpcNextPage; resp.OpcNextPage == nil {
				break
			}
		}
	}

	return endpoints, nil
}

// ApplyChanges applies a given set of changes to a given zone.
func (p *OCIProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
	log.Debugf("Processing chages: %+v", changes)

	ops := []dns.RecordOperation{}
	ops = append(ops, p.newFilteredRecordOperations(changes.Create, dns.RecordOperationOperationAdd)...)

	ops = append(ops, p.newFilteredRecordOperations(changes.UpdateNew, dns.RecordOperationOperationAdd)...)
	ops = append(ops, p.newFilteredRecordOperations(changes.UpdateOld, dns.RecordOperationOperationRemove)...)

	ops = append(ops, p.newFilteredRecordOperations(changes.Delete, dns.RecordOperationOperationRemove)...)

	if len(ops) == 0 {
		log.Info("All records are already up to date")
		return nil
	}

	zones, err := p.zones(ctx)
	if err != nil {
		return errors.Wrap(err, "fetching zones")
	}

	// Separate into per-zone change sets to be passed to OCI API.
	opsByZone := operationsByZone(zones, ops)
	for zoneID, ops := range opsByZone {
		log.Infof("Change zone: %q", zoneID)
		for _, op := range ops {
			log.Info(op)
		}
	}

	if p.dryRun {
		return nil
	}

	for zoneID, ops := range opsByZone {
		if _, err := p.client.PatchZoneRecords(ctx, dns.PatchZoneRecordsRequest{
			CompartmentId:           &p.cfg.CompartmentID,
			ZoneNameOrId:            &zoneID,
			PatchZoneRecordsDetails: dns.PatchZoneRecordsDetails{Items: ops},
		}); err != nil {
			return err
		}
	}

	return nil
}

// newRecordOperation returns a RecordOperation based on a given endpoint.
func newRecordOperation(ep *endpoint.Endpoint, opType dns.RecordOperationOperationEnum) dns.RecordOperation {
	targets := make([]string, len(ep.Targets))
	copy(targets, []string(ep.Targets))
	if ep.RecordType == endpoint.RecordTypeCNAME {
		targets[0] = ensureTrailingDot(targets[0])
	}
	rdata := strings.Join(targets, " ")

	ttl := ociRecordTTL
	if ep.RecordTTL.IsConfigured() {
		ttl = int(ep.RecordTTL)
	}

	return dns.RecordOperation{
		Domain:    &ep.DNSName,
		Rdata:     &rdata,
		Ttl:       &ttl,
		Rtype:     &ep.RecordType,
		Operation: opType,
	}
}

// operationsByZone segments a slice of RecordOperations by their zone.
func operationsByZone(zones map[string]dns.ZoneSummary, ops []dns.RecordOperation) map[string][]dns.RecordOperation {
	changes := make(map[string][]dns.RecordOperation)

	zoneNameIDMapper := zoneIDName{}
	for _, z := range zones {
		zoneNameIDMapper.Add(*z.Id, *z.Name)
		changes[*z.Id] = []dns.RecordOperation{}
	}

	for _, op := range ops {
		if zoneID, _ := zoneNameIDMapper.FindZone(*op.Domain); zoneID != "" {
			changes[zoneID] = append(changes[zoneID], op)
		} else {
			log.Warnf("No matching zone for record operation %s", op)
		}
	}

	// Remove zones that don't have any changes.
	for zone, ops := range changes {
		if len(ops) == 0 {
			delete(changes, zone)
		}
	}

	return changes
}
<|endoftext|>


<commit_before>package data

import (
	"time"

	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type Memeber struct {
	ID        bson.ObjectId `bson:"_id"`
	ProjectID bson.ObjectId `bson:"project_id"`
	AccountID bson.ObjectId `bson:"account_id"`
	InviterID bson.ObjectId `bson:"inviter_id"`
	InvitedAt time.Time     `bson:"invited_at"`

	ModifiedAt time.Time `bson:"modified_at"`
	CreatedAt  time.Time `bson:"created_at"`
}

func GetMember(id bson.ObjectId) (*Memeber, error) {
	mem := Memeber{}
	err := sess.DB("").C(memberC).FindId(id).One(&mem)
	if err == mgo.ErrNotFound {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	return &mem, nil
}

func ListMembersProject(projectID bson.ObjectId, skip, limit int) ([]Memeber, error) {
	mems := []Memeber{}
	err := sess.DB("").C(memberC).
		Find(bson.M{"project_id": projectID}).
		Skip(skip).
		Limit(limit).
		Sort("-created_at").
		All(&mems)
	if err != nil {
		return nil, err
	}
	return mems, nil
}

func (m *Memeber) Put() error {
	m.ModifiedAt = time.Now()

	if m.ID == "" {
		m.ID = bson.NewObjectId()
		m.CreatedAt = m.ModifiedAt
	}
	_, err := sess.DB("").C(memberC).UpsertId(m.ID, m)
	return err
}
<commit_msg>Implement Account and Inviter method in member<commit_after>package data

import (
	"time"

	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type Memeber struct {
	ID        bson.ObjectId `bson:"_id"`
	ProjectID bson.ObjectId `bson:"project_id"`
	AccountID bson.ObjectId `bson:"account_id"`
	InviterID bson.ObjectId `bson:"inviter_id"`
	InvitedAt time.Time     `bson:"invited_at"`

	ModifiedAt time.Time `bson:"modified_at"`
	CreatedAt  time.Time `bson:"created_at"`
}

func GetMember(id bson.ObjectId) (*Memeber, error) {
	mem := Memeber{}
	err := sess.DB("").C(memberC).FindId(id).One(&mem)
	if err == mgo.ErrNotFound {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	return &mem, nil
}

func ListMembersProject(projectID bson.ObjectId, skip, limit int) ([]Memeber, error) {
	mems := []Memeber{}
	err := sess.DB("").C(memberC).
		Find(bson.M{"project_id": projectID}).
		Skip(skip).
		Limit(limit).
		Sort("-created_at").
		All(&mems)
	if err != nil {
		return nil, err
	}
	return mems, nil
}

func (m *Memeber) Account() (*Account, error) {
	return GetAccount(m.AccountID)
}

func (m *Memeber) Inviter() (*Account, error) {
	return GetAccount(m.InviterID)
}

func (m *Memeber) Put() error {
	m.ModifiedAt = time.Now()

	if m.ID == "" {
		m.ID = bson.NewObjectId()
		m.CreatedAt = m.ModifiedAt
	}
	_, err := sess.DB("").C(memberC).UpsertId(m.ID, m)
	return err
}
<|endoftext|>


<commit_before>// s3stub is a simple server that will write a local file
// on PUT and retrieve files with GET
package main

import (
	"crypto/sha256"
	"flag"
	"fmt"
	"github.com/gorilla/mux"
	"hash"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
)

var Root string

type ReadHasher struct {
	h hash.Hash
	r io.Reader
}

func (rh *ReadHasher) Read(buf []byte) (n int, err error) {
	n, err = rh.r.Read(buf)
	if err != nil {
		return n, err
	}

	rh.h.Write(buf[:n])

	return
}

func (rh *ReadHasher) Sum(b []byte) []byte {
	return rh.h.Sum(b)
}

func main() {
	var bind string

	flag.StringVar(&bind, "b", "127.0.0.1:8000", "The address to bind to")
	flag.StringVar(&Root, "r", "", "The root path of the server")
	flag.Parse()

	if Root == "" {
		fmt.Println("s3stub:")
		flag.PrintDefaults()
		return
	}

	r := mux.NewRouter()
	r.HandleFunc("/", list).Methods("GET")
	r.HandleFunc("/{path:.+}", download).Methods("GET")
	r.HandleFunc("/{path:.+}", exists).Methods("HEAD")
	r.HandleFunc("/{path:.+}", upload).Methods("PUT")
	r.HandleFunc("/{path:.+}", delete).Methods("DELETE")

	http.Handle("/", r)

	log.Println("Listening on:", bind)
	log.Println("Root:", Root)
	log.Fatal(http.ListenAndServe(bind, nil))
}

func download(w http.ResponseWriter, r *http.Request) {
	log.Println("GET", r.URL.Path)
	filename := path.Join(Root, r.URL.Path)

	fp, err := os.Open(filename)
	if err != nil {
		if os.IsNotExist(err) {
			http.Error(w, err.Error(), 404)
		} else {
			http.Error(w, err.Error(), 500)
		}
		return
	}
	defer fp.Close()

	io.Copy(w, fp)
}

func exists(w http.ResponseWriter, r *http.Request) {
	log.Println("HEAD", r.URL.Path)
	filename := path.Join(Root, r.URL.Path)

	fp, err := os.Open(filename)
	if err != nil {
		if os.IsNotExist(err) {
			w.WriteHeader(404)
		} else {
			log.Println("HEAD", r.URL.Path, err.Error())
			w.WriteHeader(500)
		}
		return
	}
	defer fp.Close()

	w.WriteHeader(204)
}

func list(w http.ResponseWriter, r *http.Request) {
	log.Println("GET", r.URL.Path)

	printpath := func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		fmt.Fprintf(w, "%s\n", strings.Replace(path, Root, "/", -1))
		return nil
	}

	filepath.Walk(Root, printpath)
}

func upload(w http.ResponseWriter, r *http.Request) {
	log.Println("PUT", r.URL.Path)
	filename := path.Join(Root, r.URL.Path)

	os.MkdirAll(path.Dir(filename), 0700)

	fp, err := os.Create(filename)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	defer fp.Close()

	bodyReader := &ReadHasher{h: sha256.New(), r: r.Body}
	io.Copy(fp, bodyReader)
	fmt.Fprintf(w, "%x", bodyReader.Sum(nil))
}

func delete(w http.ResponseWriter, r *http.Request) {
	log.Println("DELETE", r.URL.Path)
	filename := path.Join(Root, r.URL.Path)

	err := os.Remove(filename)
	if err != nil {
		if os.IsNotExist(err) {
			http.Error(w, err.Error(), 404)
		} else {
			fmt.Println("wat")
			http.Error(w, err.Error(), 500)
		}
		return
	}

	w.WriteHeader(204)
}
<commit_msg>Fixed double slash on listing<commit_after>// s3stub is a simple server that will write a local file
// on PUT and retrieve files with GET
package main

import (
	"crypto/sha256"
	"flag"
	"fmt"
	"github.com/gorilla/mux"
	"hash"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
)

var Root string

type ReadHasher struct {
	h hash.Hash
	r io.Reader
}

func (rh *ReadHasher) Read(buf []byte) (n int, err error) {
	n, err = rh.r.Read(buf)
	if err != nil {
		return n, err
	}

	rh.h.Write(buf[:n])

	return
}

func (rh *ReadHasher) Sum(b []byte) []byte {
	return rh.h.Sum(b)
}

func main() {
	var bind string

	flag.StringVar(&bind, "b", "127.0.0.1:8000", "The address to bind to")
	flag.StringVar(&Root, "r", "", "The root path of the server")
	flag.Parse()

	if Root == "" {
		fmt.Println("s3stub:")
		flag.PrintDefaults()
		return
	}

	r := mux.NewRouter()
	r.HandleFunc("/", list).Methods("GET")
	r.HandleFunc("/{path:.+}", download).Methods("GET")
	r.HandleFunc("/{path:.+}", exists).Methods("HEAD")
	r.HandleFunc("/{path:.+}", upload).Methods("PUT")
	r.HandleFunc("/{path:.+}", delete).Methods("DELETE")

	http.Handle("/", r)

	log.Println("Listening on:", bind)
	log.Println("Root:", Root)
	log.Fatal(http.ListenAndServe(bind, nil))
}

func download(w http.ResponseWriter, r *http.Request) {
	log.Println("GET", r.URL.Path)
	filename := path.Join(Root, r.URL.Path)

	fp, err := os.Open(filename)
	if err != nil {
		if os.IsNotExist(err) {
			http.Error(w, err.Error(), 404)
		} else {
			http.Error(w, err.Error(), 500)
		}
		return
	}
	defer fp.Close()

	io.Copy(w, fp)
}

func exists(w http.ResponseWriter, r *http.Request) {
	log.Println("HEAD", r.URL.Path)
	filename := path.Join(Root, r.URL.Path)

	fp, err := os.Open(filename)
	if err != nil {
		if os.IsNotExist(err) {
			w.WriteHeader(404)
		} else {
			log.Println("HEAD", r.URL.Path, err.Error())
			w.WriteHeader(500)
		}
		return
	}
	defer fp.Close()

	w.WriteHeader(204)
}

func list(w http.ResponseWriter, r *http.Request) {
	log.Println("GET", r.URL.Path)

	printpath := func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		fmt.Fprintf(w, "%s\n", strings.Replace(path, Root, "", 1))
		return nil
	}

	filepath.Walk(Root, printpath)
}

func upload(w http.ResponseWriter, r *http.Request) {
	log.Println("PUT", r.URL.Path)
	filename := path.Join(Root, r.URL.Path)

	os.MkdirAll(path.Dir(filename), 0700)

	fp, err := os.Create(filename)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	defer fp.Close()

	bodyReader := &ReadHasher{h: sha256.New(), r: r.Body}
	io.Copy(fp, bodyReader)
	fmt.Fprintf(w, "%x", bodyReader.Sum(nil))
}

func delete(w http.ResponseWriter, r *http.Request) {
	log.Println("DELETE", r.URL.Path)
	filename := path.Join(Root, r.URL.Path)

	err := os.Remove(filename)
	if err != nil {
		if os.IsNotExist(err) {
			http.Error(w, err.Error(), 404)
		} else {
			fmt.Println("wat")
			http.Error(w, err.Error(), 500)
		}
		return
	}

	w.WriteHeader(204)
}
<|endoftext|>


<commit_before>// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"os"
	"time"

	"github.com/golang/protobuf/proto"
	"github.com/luci/luci-go/client/logdog/annotee"
	"github.com/luci/luci-go/client/logdog/annotee/executor"
	"github.com/luci/luci-go/client/logdog/butlerlib/bootstrap"
	"github.com/luci/luci-go/client/logdog/butlerlib/streamclient"
	"github.com/luci/luci-go/client/logdog/butlerlib/streamproto"
	"github.com/luci/luci-go/common/clock/clockflag"
	"github.com/luci/luci-go/common/config"
	"github.com/luci/luci-go/common/logdog/types"
	log "github.com/luci/luci-go/common/logging"
	"github.com/luci/luci-go/common/logging/gologger"
	"golang.org/x/net/context"
)

const (
	// configErrorReturnCode is returned when there is an error with Annotee's
	// command-line configuration.
	configErrorReturnCode = 2

	// runtimeErrorReturnCode is returned when the execution fails due to an
	// Annotee runtime error. This is intended to help differentiate Annotee
	// errors from passthrough bootstrapped subprocess errors.
	//
	// This will only be returned for runtime errors. If there is a flag error
	// or a configuration error, standard Annotee return codes (likely to overlap
	// with standard process return codes) will be used.
	//
	// This value has been chosen so as not to conflict with LogDog Butler runtime
	// error return code, allowing users to differentiate between Butler and
	// Annotee errors.
	runtimeErrorReturnCode = 251

	// defaultAnnotationInterval is the default annotation interval value.
	defaultAnnotationInterval = 30 * time.Second
)

type application struct {
	context.Context

	annotate           annotationMode
	jsonArgsPath       string
	butlerStreamServer string
	tee                bool
	printSummary       bool
	testingDir         string
	annotationInterval clockflag.Duration
	project            config.ProjectName
	nameBase           streamproto.StreamNameFlag
	prefix             streamproto.StreamNameFlag
	logdogHost         string

	bootstrap *bootstrap.Bootstrap
}

func (a *application) addToFlagSet(fs *flag.FlagSet) {
	fs.StringVar(&a.jsonArgsPath, "json-args-path", "",
		"If specified, this is a JSON file containing the full command to run as an "+
			"array of strings.")
	fs.Var(&a.annotate, "annotate",
		"Annotation handling mode. Options are: "+annotationFlagEnum.Choices())
	fs.StringVar(&a.butlerStreamServer, "butler-stream-server", "",
		"The Butler stream server location. If empty, Annotee will check for Butler "+
			"bootstrapping and extract the stream server from that.")
	fs.BoolVar(&a.tee, "tee", true,
		"Tee the bootstrapped process' STDOUT/STDERR streams.")
	fs.BoolVar(&a.printSummary, "print-summary", true,
		"Print the annotation protobufs that were emitted at the end.")
	fs.StringVar(&a.testingDir, "testing-dir", "",
		"Rather than coupling to a Butler instance, output generated annotations "+
			"and streams to this directory.")
	fs.Var(&a.annotationInterval, "annotation-interval",
		"Buffer annotation updates for this amount of time. <=0 sends every update.")
	fs.Var(&a.project, "project", "The log prefix's project name (required).")
	fs.Var(&a.nameBase, "name-base", "Base stream name to prepend to generated names.")
	fs.Var(&a.prefix, "prefix", "The log stream prefix. If missing, one will be inferred from bootstrap.")
	fs.StringVar(&a.logdogHost, "logdog-host", "",
		"LogDog Coordinator host name. If supplied, log viewing links will be generated.")
}

func (a *application) loadJSONArgs() ([]string, error) {
	fd, err := os.Open(a.jsonArgsPath)
	if err != nil {
		return nil, err
	}
	defer fd.Close()

	dec := json.NewDecoder(fd)
	args := []string(nil)
	if err := dec.Decode(&args); err != nil {
		return nil, err
	}
	return args, nil
}

func (a *application) getStreamClient() (streamclient.Client, error) {
	if a.testingDir != "" {
		return newFilesystemClient(a.testingDir)
	}

	if a.butlerStreamServer != "" {
		return streamclient.New(a.butlerStreamServer)
	}

	// Check our bootstrap.
	if a.bootstrap != nil && a.bootstrap.Client != nil {
		return a.bootstrap.Client, nil
	}

	return nil, errors.New("unable to identify stream client")
}

func mainImpl(args []string) int {
	ctx := gologger.StdConfig.Use(context.Background())

	logFlags := log.Config{
		Level: log.Warning,
	}
	a := application{
		Context:            ctx,
		annotationInterval: clockflag.Duration(defaultAnnotationInterval),
	}

	// Determine bootstrapped process arguments.
	var err error
	a.bootstrap, err = bootstrap.Get()
	if err != nil {
		log.WithError(err).Warningf(a, "Could not get LogDog Butler bootstrap information.")
	}

	fs := &flag.FlagSet{}
	logFlags.AddFlags(fs)
	a.addToFlagSet(fs)
	if err := fs.Parse(args); err != nil {
		log.WithError(err).Errorf(a, "Failed to parse flags.")
		return configErrorReturnCode
	}
	a.Context = logFlags.Set(a.Context)

	client, err := a.getStreamClient()
	if err != nil {
		log.WithError(err).Errorf(a, "Failed to get stream client instance.")
		return configErrorReturnCode
	}

	prefix := types.StreamName(a.prefix)
	if prefix == "" && a.bootstrap != nil {
		prefix = a.bootstrap.Prefix
	}

	// TODO(dnj): Require project attribute.
	if a.project == "" {
		a.project = a.bootstrap.Project
	}

	args = fs.Args()
	if a.jsonArgsPath != "" {
		if len(args) > 0 {
			log.Fields{
				"commandLineArgs": args,
				"jsonArgsPath":    a.jsonArgsPath,
			}.Errorf(a, "Cannot specify both JSON and command-line arguments.")
			return configErrorReturnCode
		}

		args, err = a.loadJSONArgs()
		if err != nil {
			log.Fields{
				log.ErrorKey:   err,
				"jsonArgsPath": a.jsonArgsPath,
			}.Errorf(a, "Failed to load JSON arguments.")
			return configErrorReturnCode
		}
	}
	if len(args) == 0 {
		log.Errorf(a, "No command-line arguments were supplied.")
		return configErrorReturnCode
	}

	// Translate "<=0" flag option to Processor's "0", indicating that every
	// update should be sent.
	if a.annotationInterval < 0 {
		a.annotationInterval = 0
	}

	// Initialize our link generator, if we can.
	linkGen := &coordinatorLinkGenerator{
		base:    types.StreamName(a.nameBase),
		project: a.project,
		prefix:  prefix,
	}
	if !linkGen.canGenerateLinks() {
		linkGen = nil
	}

	e := executor.Executor{
		Options: annotee.Options{
			Client:                 client,
			LinkGenerator:          linkGen,
			MetadataUpdateInterval: time.Duration(a.annotationInterval),
		},

		Annotate: executor.AnnotationMode(a.annotate),
		Stdin:    os.Stdin,
	}
	if a.tee {
		e.TeeStdout = os.Stdout
		e.TeeStderr = os.Stderr
	}
	if err := e.Run(a, args); err != nil {
		log.Fields{
			log.ErrorKey: err,
			"returnCode": e.ReturnCode(),
		}.Errorf(a, "Failed during execution.")
	}

	// Display a summary!
	if a.printSummary {
		for _, s := range e.Steps() {
			fmt.Printf("=== Annotee: %q ===\n", s.StepComponent.Name)
			fmt.Println(proto.MarshalTextString(s))
		}
	}

	if !e.Executed() {
		return runtimeErrorReturnCode
	}
	return e.ReturnCode()
}

func main() {
	os.Exit(mainImpl(os.Args[1:]))
}
<commit_msg>LogDog: Fix annotee passing name base.<commit_after>// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"os"
	"time"

	"github.com/golang/protobuf/proto"
	"github.com/luci/luci-go/client/logdog/annotee"
	"github.com/luci/luci-go/client/logdog/annotee/executor"
	"github.com/luci/luci-go/client/logdog/butlerlib/bootstrap"
	"github.com/luci/luci-go/client/logdog/butlerlib/streamclient"
	"github.com/luci/luci-go/client/logdog/butlerlib/streamproto"
	"github.com/luci/luci-go/common/clock/clockflag"
	"github.com/luci/luci-go/common/config"
	"github.com/luci/luci-go/common/logdog/types"
	log "github.com/luci/luci-go/common/logging"
	"github.com/luci/luci-go/common/logging/gologger"
	"golang.org/x/net/context"
)

const (
	// configErrorReturnCode is returned when there is an error with Annotee's
	// command-line configuration.
	configErrorReturnCode = 2

	// runtimeErrorReturnCode is returned when the execution fails due to an
	// Annotee runtime error. This is intended to help differentiate Annotee
	// errors from passthrough bootstrapped subprocess errors.
	//
	// This will only be returned for runtime errors. If there is a flag error
	// or a configuration error, standard Annotee return codes (likely to overlap
	// with standard process return codes) will be used.
	//
	// This value has been chosen so as not to conflict with LogDog Butler runtime
	// error return code, allowing users to differentiate between Butler and
	// Annotee errors.
	runtimeErrorReturnCode = 251

	// defaultAnnotationInterval is the default annotation interval value.
	defaultAnnotationInterval = 30 * time.Second
)

type application struct {
	context.Context

	annotate           annotationMode
	jsonArgsPath       string
	butlerStreamServer string
	tee                bool
	printSummary       bool
	testingDir         string
	annotationInterval clockflag.Duration
	project            config.ProjectName
	nameBase           streamproto.StreamNameFlag
	prefix             streamproto.StreamNameFlag
	logdogHost         string

	bootstrap *bootstrap.Bootstrap
}

func (a *application) addToFlagSet(fs *flag.FlagSet) {
	fs.StringVar(&a.jsonArgsPath, "json-args-path", "",
		"If specified, this is a JSON file containing the full command to run as an "+
			"array of strings.")
	fs.Var(&a.annotate, "annotate",
		"Annotation handling mode. Options are: "+annotationFlagEnum.Choices())
	fs.StringVar(&a.butlerStreamServer, "butler-stream-server", "",
		"The Butler stream server location. If empty, Annotee will check for Butler "+
			"bootstrapping and extract the stream server from that.")
	fs.BoolVar(&a.tee, "tee", true,
		"Tee the bootstrapped process' STDOUT/STDERR streams.")
	fs.BoolVar(&a.printSummary, "print-summary", true,
		"Print the annotation protobufs that were emitted at the end.")
	fs.StringVar(&a.testingDir, "testing-dir", "",
		"Rather than coupling to a Butler instance, output generated annotations "+
			"and streams to this directory.")
	fs.Var(&a.annotationInterval, "annotation-interval",
		"Buffer annotation updates for this amount of time. <=0 sends every update.")
	fs.Var(&a.project, "project", "The log prefix's project name (required).")
	fs.Var(&a.nameBase, "name-base", "Base stream name to prepend to generated names.")
	fs.Var(&a.prefix, "prefix", "The log stream prefix. If missing, one will be inferred from bootstrap.")
	fs.StringVar(&a.logdogHost, "logdog-host", "",
		"LogDog Coordinator host name. If supplied, log viewing links will be generated.")
}

func (a *application) loadJSONArgs() ([]string, error) {
	fd, err := os.Open(a.jsonArgsPath)
	if err != nil {
		return nil, err
	}
	defer fd.Close()

	dec := json.NewDecoder(fd)
	args := []string(nil)
	if err := dec.Decode(&args); err != nil {
		return nil, err
	}
	return args, nil
}

func (a *application) getStreamClient() (streamclient.Client, error) {
	if a.testingDir != "" {
		return newFilesystemClient(a.testingDir)
	}

	if a.butlerStreamServer != "" {
		return streamclient.New(a.butlerStreamServer)
	}

	// Check our bootstrap.
	if a.bootstrap != nil && a.bootstrap.Client != nil {
		return a.bootstrap.Client, nil
	}

	return nil, errors.New("unable to identify stream client")
}

func mainImpl(args []string) int {
	ctx := gologger.StdConfig.Use(context.Background())

	logFlags := log.Config{
		Level: log.Warning,
	}
	a := application{
		Context:            ctx,
		annotationInterval: clockflag.Duration(defaultAnnotationInterval),
	}

	// Determine bootstrapped process arguments.
	var err error
	a.bootstrap, err = bootstrap.Get()
	if err != nil {
		log.WithError(err).Warningf(a, "Could not get LogDog Butler bootstrap information.")
	}

	fs := &flag.FlagSet{}
	logFlags.AddFlags(fs)
	a.addToFlagSet(fs)
	if err := fs.Parse(args); err != nil {
		log.WithError(err).Errorf(a, "Failed to parse flags.")
		return configErrorReturnCode
	}
	a.Context = logFlags.Set(a.Context)

	client, err := a.getStreamClient()
	if err != nil {
		log.WithError(err).Errorf(a, "Failed to get stream client instance.")
		return configErrorReturnCode
	}

	prefix := types.StreamName(a.prefix)
	if prefix == "" && a.bootstrap != nil {
		prefix = a.bootstrap.Prefix
	}

	// TODO(dnj): Require project attribute.
	if a.project == "" {
		a.project = a.bootstrap.Project
	}

	args = fs.Args()
	if a.jsonArgsPath != "" {
		if len(args) > 0 {
			log.Fields{
				"commandLineArgs": args,
				"jsonArgsPath":    a.jsonArgsPath,
			}.Errorf(a, "Cannot specify both JSON and command-line arguments.")
			return configErrorReturnCode
		}

		args, err = a.loadJSONArgs()
		if err != nil {
			log.Fields{
				log.ErrorKey:   err,
				"jsonArgsPath": a.jsonArgsPath,
			}.Errorf(a, "Failed to load JSON arguments.")
			return configErrorReturnCode
		}
	}
	if len(args) == 0 {
		log.Errorf(a, "No command-line arguments were supplied.")
		return configErrorReturnCode
	}

	// Translate "<=0" flag option to Processor's "0", indicating that every
	// update should be sent.
	if a.annotationInterval < 0 {
		a.annotationInterval = 0
	}

	// Initialize our link generator, if we can.
	linkGen := &coordinatorLinkGenerator{
		base:    types.StreamName(a.nameBase),
		project: a.project,
		prefix:  prefix,
	}
	if !linkGen.canGenerateLinks() {
		linkGen = nil
	}

	e := executor.Executor{
		Options: annotee.Options{
			Base:                   types.StreamName(a.nameBase),
			LinkGenerator:          linkGen,
			Client:                 client,
			MetadataUpdateInterval: time.Duration(a.annotationInterval),
		},

		Annotate: executor.AnnotationMode(a.annotate),
		Stdin:    os.Stdin,
	}
	if a.tee {
		e.TeeStdout = os.Stdout
		e.TeeStderr = os.Stderr
	}
	if err := e.Run(a, args); err != nil {
		log.Fields{
			log.ErrorKey: err,
			"returnCode": e.ReturnCode(),
		}.Errorf(a, "Failed during execution.")
	}

	// Display a summary!
	if a.printSummary {
		for _, s := range e.Steps() {
			fmt.Printf("=== Annotee: %q ===\n", s.StepComponent.Name)
			fmt.Println(proto.MarshalTextString(s))
		}
	}

	if !e.Executed() {
		return runtimeErrorReturnCode
	}
	return e.ReturnCode()
}

func main() {
	os.Exit(mainImpl(os.Args[1:]))
}
<|endoftext|>