<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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 e2e

import (
	"encoding/json"
	"errors"
	"fmt"
	"path/filepath"
	"strings"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const (
	nautilusImage            = "gcr.io/google_containers/update-demo:nautilus"
	kittenImage              = "gcr.io/google_containers/update-demo:kitten"
	updateDemoSelector       = "name=update-demo"
	updateDemoContainer      = "update-demo"
	frontendSelector         = "name=frontend"
	redisMasterSelector      = "name=redis-master"
	redisSlaveSelector       = "name=redis-slave"
	kubectlProxyPort         = 8011
	guestbookStartupTimeout  = 10 * time.Minute
	guestbookResponseTimeout = 3 * time.Minute
)

var _ = Describe("Kubectl client", func() {
	defer GinkgoRecover()
	var c *client.Client
	var ns string
	var testingNs *api.Namespace
	BeforeEach(func() {
		var err error
		c, err = loadClient()
		expectNoError(err)
		testingNs, err = createTestingNS("Kubectl client", c)
		ns = testingNs.Name
		Expect(err).NotTo(HaveOccurred())
	})

	AfterEach(func() {
		By(fmt.Sprintf("Destroying namespace for this suite %v", ns))
		if err := c.Namespaces().Delete(ns); err != nil {
			Failf("Couldn't delete ns %s", err)
		}
	})

	Describe("Update Demo", func() {
		var updateDemoRoot, nautilusPath, kittenPath string
		BeforeEach(func() {
			updateDemoRoot = filepath.Join(testContext.RepoRoot, "examples/update-demo")
			nautilusPath = filepath.Join(updateDemoRoot, "nautilus-rc.yaml")
			kittenPath = filepath.Join(updateDemoRoot, "kitten-rc.yaml")
		})

		It("should create and stop a replication controller", func() {
			defer cleanup(nautilusPath, ns, updateDemoSelector)

			By("creating a replication controller")
			runKubectl("create", "-f", nautilusPath, fmt.Sprintf("--namespace=%v", ns))
			validateController(c, nautilusImage, 2, "update-demo", updateDemoSelector, getUDData("nautilus.jpg", ns), ns)
		})

		It("should scale a replication controller", func() {
			defer cleanup(nautilusPath, ns, updateDemoSelector)

			By("creating a replication controller")
			runKubectl("create", "-f", nautilusPath, fmt.Sprintf("--namespace=%v", ns))
			validateController(c, nautilusImage, 2, "update-demo", updateDemoSelector, getUDData("nautilus.jpg", ns), ns)
			By("scaling down the replication controller")
			runKubectl("scale", "rc", "update-demo-nautilus", "--replicas=1", fmt.Sprintf("--namespace=%v", ns))
			validateController(c, nautilusImage, 1, "update-demo", updateDemoSelector, getUDData("nautilus.jpg", ns), ns)
			By("scaling up the replication controller")
			runKubectl("scale", "rc", "update-demo-nautilus", "--replicas=2", fmt.Sprintf("--namespace=%v", ns))
			validateController(c, nautilusImage, 2, "update-demo", updateDemoSelector, getUDData("nautilus.jpg", ns), ns)
		})

		It("should do a rolling update of a replication controller", func() {
			By("creating the initial replication controller")
			runKubectl("create", "-f", nautilusPath, fmt.Sprintf("--namespace=%v", ns))
			validateController(c, nautilusImage, 2, "update-demo", updateDemoSelector, getUDData("nautilus.jpg", ns), ns)
			By("rolling-update to new replication controller")
			runKubectl("rolling-update", "update-demo-nautilus", "--update-period=1s", "-f", kittenPath, fmt.Sprintf("--namespace=%v", ns))
			validateController(c, kittenImage, 2, "update-demo", updateDemoSelector, getUDData("kitten.jpg", ns), ns)
			// Everything will hopefully be cleaned up when the namespace is deleted.
		})
	})

	Describe("Guestbook application", func() {
		var guestbookPath string
		BeforeEach(func() {
			guestbookPath = filepath.Join(testContext.RepoRoot, "examples/guestbook")
		})

		It("should create and stop a working application", func() {
			if !providerIs("gce", "gke", "aws") {
				By(fmt.Sprintf("Skipping guestbook, uses createExternalLoadBalancer, a (gce|gke|aws) feature"))
				return
			}

			defer cleanup(guestbookPath, ns, frontendSelector, redisMasterSelector, redisSlaveSelector)

			By("creating all guestbook components")
			runKubectl("create", "-f", guestbookPath, fmt.Sprintf("--namespace=%v", ns))

			By("validating guestbook app")
			validateGuestbookApp(c, ns)
		})
	})

})

func validateGuestbookApp(c *client.Client, ns string) {
	Logf("Waiting for frontend to serve content.")
	if !waitForGuestbookResponse(c, "get", "", `{"data": ""}`, guestbookStartupTimeout, ns) {
		Failf("Frontend service did not start serving content in %v seconds.", guestbookStartupTimeout.Seconds())
	}

	Logf("Trying to add a new entry to the guestbook.")
	if !waitForGuestbookResponse(c, "set", "TestEntry", `{"message": "Updated"}`, guestbookResponseTimeout, ns) {
		Failf("Cannot added new entry in %v seconds.", guestbookResponseTimeout.Seconds())
	}

	Logf("Verifying that added entry can be retrieved.")
	if !waitForGuestbookResponse(c, "get", "", `{"data": "TestEntry"}`, guestbookResponseTimeout, ns) {
		Failf("Entry to guestbook wasn't correctly added in %v seconds.", guestbookResponseTimeout.Seconds())
	}
}

// Returns whether received expected response from guestbook on time.
func waitForGuestbookResponse(c *client.Client, cmd, arg, expectedResponse string, timeout time.Duration, ns string) bool {
	for start := time.Now(); time.Since(start) < timeout; time.Sleep(5 * time.Second) {
		res, err := makeRequestToGuestbook(c, cmd, arg, ns)
		if err == nil && res == expectedResponse {
			return true
		}
	}
	return false
}

func makeRequestToGuestbook(c *client.Client, cmd, value string, ns string) (string, error) {
	result, err := c.Get().
		Prefix("proxy").
		Namespace(ns).
		Resource("services").
		Name("frontend").
		Suffix("/index.php").
		Param("cmd", cmd).
		Param("key", "messages").
		Param("value", value).
		Do().
		Raw()
	return string(result), err
}

type updateDemoData struct {
	Image string
}

// getUDData creates a validator function based on the input string (i.e. kitten.jpg).
// For example, if you send "kitten.jpg", this function veridies that the image jpg = kitten.jpg
// in the container's json field.
func getUDData(jpgExpected string, ns string) func(*client.Client, string) error {

	// getUDData validates data.json in the update-demo (returns nil if data is ok).
	return func(c *client.Client, podID string) error {
		Logf("validating pod %s", podID)
		body, err := c.Get().
			Prefix("proxy").
			Namespace(ns).
			Resource("pods").
			Name(podID).
			Suffix("data.json").
			Do().
			Raw()
		if err != nil {
			return err
		}
		Logf("got data: %s", body)
		var data updateDemoData
		if err := json.Unmarshal(body, &data); err != nil {
			return err
		}
		Logf("Unmarshalled json jpg/img => %s , expecting %s .", data, jpgExpected)
		if strings.Contains(data.Image, jpgExpected) {
			return nil
		} else {
			return errors.New(fmt.Sprintf("data served up in container is innaccurate, %s didn't contain %s", data, jpgExpected))
		}
	}
}
<commit_msg>Fix bad namespace<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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 e2e

import (
	"encoding/json"
	"errors"
	"fmt"
	"path/filepath"
	"strings"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const (
	nautilusImage            = "gcr.io/google_containers/update-demo:nautilus"
	kittenImage              = "gcr.io/google_containers/update-demo:kitten"
	updateDemoSelector       = "name=update-demo"
	updateDemoContainer      = "update-demo"
	frontendSelector         = "name=frontend"
	redisMasterSelector      = "name=redis-master"
	redisSlaveSelector       = "name=redis-slave"
	kubectlProxyPort         = 8011
	guestbookStartupTimeout  = 10 * time.Minute
	guestbookResponseTimeout = 3 * time.Minute
)

var _ = Describe("Kubectl client", func() {
	defer GinkgoRecover()
	var c *client.Client
	var ns string
	var testingNs *api.Namespace
	BeforeEach(func() {
		var err error
		c, err = loadClient()
		expectNoError(err)
		testingNs, err = createTestingNS("kubectl", c)
		ns = testingNs.Name
		Expect(err).NotTo(HaveOccurred())
	})

	AfterEach(func() {
		By(fmt.Sprintf("Destroying namespace for this suite %v", ns))
		if err := c.Namespaces().Delete(ns); err != nil {
			Failf("Couldn't delete ns %s", err)
		}
	})

	Describe("Update Demo", func() {
		var updateDemoRoot, nautilusPath, kittenPath string
		BeforeEach(func() {
			updateDemoRoot = filepath.Join(testContext.RepoRoot, "examples/update-demo")
			nautilusPath = filepath.Join(updateDemoRoot, "nautilus-rc.yaml")
			kittenPath = filepath.Join(updateDemoRoot, "kitten-rc.yaml")
		})

		It("should create and stop a replication controller", func() {
			defer cleanup(nautilusPath, ns, updateDemoSelector)

			By("creating a replication controller")
			runKubectl("create", "-f", nautilusPath, fmt.Sprintf("--namespace=%v", ns))
			validateController(c, nautilusImage, 2, "update-demo", updateDemoSelector, getUDData("nautilus.jpg", ns), ns)
		})

		It("should scale a replication controller", func() {
			defer cleanup(nautilusPath, ns, updateDemoSelector)

			By("creating a replication controller")
			runKubectl("create", "-f", nautilusPath, fmt.Sprintf("--namespace=%v", ns))
			validateController(c, nautilusImage, 2, "update-demo", updateDemoSelector, getUDData("nautilus.jpg", ns), ns)
			By("scaling down the replication controller")
			runKubectl("scale", "rc", "update-demo-nautilus", "--replicas=1", fmt.Sprintf("--namespace=%v", ns))
			validateController(c, nautilusImage, 1, "update-demo", updateDemoSelector, getUDData("nautilus.jpg", ns), ns)
			By("scaling up the replication controller")
			runKubectl("scale", "rc", "update-demo-nautilus", "--replicas=2", fmt.Sprintf("--namespace=%v", ns))
			validateController(c, nautilusImage, 2, "update-demo", updateDemoSelector, getUDData("nautilus.jpg", ns), ns)
		})

		It("should do a rolling update of a replication controller", func() {
			By("creating the initial replication controller")
			runKubectl("create", "-f", nautilusPath, fmt.Sprintf("--namespace=%v", ns))
			validateController(c, nautilusImage, 2, "update-demo", updateDemoSelector, getUDData("nautilus.jpg", ns), ns)
			By("rolling-update to new replication controller")
			runKubectl("rolling-update", "update-demo-nautilus", "--update-period=1s", "-f", kittenPath, fmt.Sprintf("--namespace=%v", ns))
			validateController(c, kittenImage, 2, "update-demo", updateDemoSelector, getUDData("kitten.jpg", ns), ns)
			// Everything will hopefully be cleaned up when the namespace is deleted.
		})
	})

	Describe("Guestbook application", func() {
		var guestbookPath string
		BeforeEach(func() {
			guestbookPath = filepath.Join(testContext.RepoRoot, "examples/guestbook")
		})

		It("should create and stop a working application", func() {
			if !providerIs("gce", "gke", "aws") {
				By(fmt.Sprintf("Skipping guestbook, uses createExternalLoadBalancer, a (gce|gke|aws) feature"))
				return
			}

			defer cleanup(guestbookPath, ns, frontendSelector, redisMasterSelector, redisSlaveSelector)

			By("creating all guestbook components")
			runKubectl("create", "-f", guestbookPath, fmt.Sprintf("--namespace=%v", ns))

			By("validating guestbook app")
			validateGuestbookApp(c, ns)
		})
	})

})

func validateGuestbookApp(c *client.Client, ns string) {
	Logf("Waiting for frontend to serve content.")
	if !waitForGuestbookResponse(c, "get", "", `{"data": ""}`, guestbookStartupTimeout, ns) {
		Failf("Frontend service did not start serving content in %v seconds.", guestbookStartupTimeout.Seconds())
	}

	Logf("Trying to add a new entry to the guestbook.")
	if !waitForGuestbookResponse(c, "set", "TestEntry", `{"message": "Updated"}`, guestbookResponseTimeout, ns) {
		Failf("Cannot added new entry in %v seconds.", guestbookResponseTimeout.Seconds())
	}

	Logf("Verifying that added entry can be retrieved.")
	if !waitForGuestbookResponse(c, "get", "", `{"data": "TestEntry"}`, guestbookResponseTimeout, ns) {
		Failf("Entry to guestbook wasn't correctly added in %v seconds.", guestbookResponseTimeout.Seconds())
	}
}

// Returns whether received expected response from guestbook on time.
func waitForGuestbookResponse(c *client.Client, cmd, arg, expectedResponse string, timeout time.Duration, ns string) bool {
	for start := time.Now(); time.Since(start) < timeout; time.Sleep(5 * time.Second) {
		res, err := makeRequestToGuestbook(c, cmd, arg, ns)
		if err == nil && res == expectedResponse {
			return true
		}
	}
	return false
}

func makeRequestToGuestbook(c *client.Client, cmd, value string, ns string) (string, error) {
	result, err := c.Get().
		Prefix("proxy").
		Namespace(ns).
		Resource("services").
		Name("frontend").
		Suffix("/index.php").
		Param("cmd", cmd).
		Param("key", "messages").
		Param("value", value).
		Do().
		Raw()
	return string(result), err
}

type updateDemoData struct {
	Image string
}

// getUDData creates a validator function based on the input string (i.e. kitten.jpg).
// For example, if you send "kitten.jpg", this function veridies that the image jpg = kitten.jpg
// in the container's json field.
func getUDData(jpgExpected string, ns string) func(*client.Client, string) error {

	// getUDData validates data.json in the update-demo (returns nil if data is ok).
	return func(c *client.Client, podID string) error {
		Logf("validating pod %s", podID)
		body, err := c.Get().
			Prefix("proxy").
			Namespace(ns).
			Resource("pods").
			Name(podID).
			Suffix("data.json").
			Do().
			Raw()
		if err != nil {
			return err
		}
		Logf("got data: %s", body)
		var data updateDemoData
		if err := json.Unmarshal(body, &data); err != nil {
			return err
		}
		Logf("Unmarshalled json jpg/img => %s , expecting %s .", data, jpgExpected)
		if strings.Contains(data.Image, jpgExpected) {
			return nil
		} else {
			return errors.New(fmt.Sprintf("data served up in container is innaccurate, %s didn't contain %s", data, jpgExpected))
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"archive/tar"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/fsouza/go-dockerclient"
)

const version = "0.1.2"

func main() {
	endpoint := "unix:///var/run/docker.sock"
	client, _ := docker.NewClient(endpoint)
	var isFiltering = false
	var filteringWord = ""
	var argc = len(os.Args)
	if argc < 2 {
		os.Exit(2)
	} else if argc == 3 {
		isFiltering = true
		filteringWord = os.Args[2]
	}

	historyList, err := client.ImageHistory(os.Args[1])
	if err != nil {
		fmt.Println("Error happens at client.ImageHistory")
		os.Exit(2)
	}
	// First CreatedBy newer to older
	var createdByHistoryList = []string{}
	//
	var etcpasswdmap = map[string]string{}
	var etcgroupmap = map[string]string{}
	// WORKAROUND
	var etcpasswdstring = ""
	var etcgroupstring = ""
	// WORKAROUND
	var etcpasswduidnamemap = map[int]string{}
	var etcgroupgidnamemap = map[int]string{}
	// layer ID -> json
	var jsonMap = map[string]string{}
	var allJsonMap = map[string]string{}
	var layerTarMap = map[string][]*tar.Header{}
	var first = false
	for _, history := range historyList {
		createdByHistoryList = append(createdByHistoryList, history.CreatedBy)
		image, err := client.InspectImage(history.ID)
		if err != nil || first {
			// continue to create CreatedBy list
			continue
		}
		if image != nil {
			var buf bytes.Buffer
			opts := docker.ExportImageOptions{Name: image.ID, OutputStream: &buf}
			err := client.ExportImage(opts)
			if err != nil {
				fmt.Println("Error happens at client.ExportImage")
				os.Exit(2)
			}
			r := bytes.NewReader(buf.Bytes())
			tr := tar.NewReader(r)
			var header *tar.Header
			for {
				header, err = tr.Next()
				if err == io.EOF {
					// end of image tar
					break
				}
				if err != nil {
					fmt.Println(err)
					fmt.Println("Error at tar extract")
					os.Exit(2)
				}

				buf2 := new(bytes.Buffer)
				if _, err = io.Copy(buf2, tr); err != nil {
					fmt.Println(err)
				}
				//
				var field = strings.Split(header.Name, "/")
				var layerID = field[0]
				// find json and layer.tar
				if strings.HasSuffix(header.Name, "/json") {
					var jsonstring = buf2.String()
					//var imagestring = ""
					allJsonMap[layerID] = jsonstring
					if strings.Index(jsonstring, "\"Image\":\"\"") != -1 || !first {
						jsonMap[layerID] = jsonstring
					}
				} else if strings.HasSuffix(header.Name, "layer.tar") {

					r2 := bytes.NewReader(buf2.Bytes())
					layerTar := tar.NewReader(r2)
					for {
						layerTarHeader, e4 := layerTar.Next()
						if e4 == io.EOF {
							// end of layer.tar
							break
						}
						layerTarMap[layerID] = append(layerTarMap[layerID], layerTarHeader)
						// read for etcpasswd and etcgroup
						if layerTarHeader.Name == "etc/passwd" || layerTarHeader.Name == "etc/group" {
							layerTarBuffer := new(bytes.Buffer)
							if _, err = io.Copy(layerTarBuffer, layerTar); err != nil {
								fmt.Println(err)
							}
							// TODO store json data
							var etcfilejsonstring = layerTarBuffer.String()
							// fmt.Println("FILENAME=[" + layerTarHeader.Name + "]")
							// fmt.Println(etcfilejsonstring)
							if layerTarHeader.Name == "etc/passwd" {
								etcpasswdmap[layerID] = etcfilejsonstring

								if etcpasswdstring == "" {
									etcpasswdstring = etcfilejsonstring
									var lines = strings.Split(etcpasswdstring, "\n")
									for _, line := range lines {
										var fields = strings.Split(line, ":")
										if len(fields) > 2 {
											var uid, _ = strconv.Atoi(fields[2])
											etcpasswduidnamemap[uid] = fields[0]
										}
									}
								}
							} else if layerTarHeader.Name == "etc/group" {
								etcgroupmap[layerID] = etcfilejsonstring
								if etcgroupstring == "" {
									etcgroupstring = etcfilejsonstring
									var lines = strings.Split(etcgroupstring, "\n")
									for _, line := range lines {
										var fields = strings.Split(line, ":")
										if len(fields) > 2 {
											var gid, _ = strconv.Atoi(fields[2])
											etcgroupgidnamemap[gid] = fields[0]
										}
									}
								}
							}
						}
					}
				}
			}
		}
		first = true

	}
	// fmt.Println("---- createdBy")
	// for _, createdBy := range createdByHistoryList {
	// 	fmt.Println(createdBy)
	// }
	// construct parent map
	//fmt.Println("---- jsonMap")
	var relation = map[string]string{}
	//var key string
	var value string
	var noparentid string
	for _, value = range jsonMap {
		//fmt.Println(key + " -> " + value)
		//dec := json.NewDecoder(v)
		var f interface{}
		//dec.Decode(&d)
		json.Unmarshal([]byte(value), &f)
		//fmt.Printf("%+v\n", d)
		m := f.(map[string]interface{})
		var id = m["id"]
		var parent = m["parent"]
		if parent == nil {
			noparentid = id.(string)
		} else {
			var p = parent.(string)
			_, ok := relation[p]
			if !ok {
				relation[p] = id.(string)
			}
		}
	}

	// fmt.Println("---- tarinfo")
	// fmt.Println("Start from:" + noparentid)
	var currentid = noparentid

	for {
		//fmt.Println(currentid)
		// find next
		_, ok := relation[currentid]
		if !ok {
			break
		}
		currentid = relation[currentid]
	}
	// File History List key is filename and value is layerid
	var fileHistoryListMap = map[string][]string{}
	var layerHistoryList = []string{}
	// fmt.Println("---- reconstruct")
	// fmt.Println("Start from:" + noparentid)
	currentid = noparentid
	for {
		//
		for _, layerTarHeader := range layerTarMap[currentid] {
			var filename = layerTarHeader.Name
			filename = strings.Replace(filename, ".wh.", "", -1)
			fileHistoryListMap[filename] = append(fileHistoryListMap[filename], currentid)
		}
		//
		layerHistoryList = append(layerHistoryList, currentid)
		_, ok := relation[currentid]
		if !ok {
			break
		}
		// fmt.Println(currentid + "->" + relation[currentid])
		currentid = relation[currentid]

	}
	//

	//
	// fmt.Println("---- layer")
	// reverse
	// SliceTricks · golang/go Wiki
	// https://github.com/golang/go/wiki/SliceTricks
	for i := len(layerHistoryList)/2 - 1; i >= 0; i-- {
		opp := len(layerHistoryList) - 1 - i
		layerHistoryList[i], layerHistoryList[opp] = layerHistoryList[opp], layerHistoryList[i]
	}
	// for _, layer := range layerHistoryList {
	// 	fmt.Println(layer)
	// }
	// fmt.Println("---- createdBy layer")
	// for _, createdBy := range createdByHistoryList {
	// 	fmt.Println(createdBy)
	// }
	var createdByListMap = map[string][]string{}
	var currentLayerID = ""
	var currentLayerIndex = 0
	for _, createdBy := range createdByHistoryList {
		if strings.Index(createdBy, "#(nop)") != -1 && strings.Index(createdBy, "ADD") == -1 && strings.Index(createdBy, "COPY") == -1 {
			//fmt.Println(strings.Repeat("-", 12) + " " + createdBy)
		} else {
			currentLayerID = layerHistoryList[currentLayerIndex]
			currentLayerIndex++
		}
		createdByListMap[currentLayerID] = append(createdByListMap[currentLayerID], createdBy)
	}
	//
	var layerIndex = 0

	for _, createdBy := range createdByHistoryList {
		if strings.Index(createdBy, "#(nop)") != -1 && strings.Index(createdBy, "ADD") == -1 && strings.Index(createdBy, "COPY") == -1 {
			//fmt.Println(strings.Repeat("-", 12) + " " + createdBy)
		} else {
			var layerID = layerHistoryList[layerIndex]
			for _, savedCreatedBy := range createdByListMap[layerID] {
				fmt.Println(layerID[:12] + "  " + savedCreatedBy)
			}
			//fmt.Println(allJsonMap[layerID])

			for _, layerTarHeader := range layerTarMap[layerID] {
				var filename = layerTarHeader.Name
				var deleteflag = false
				if strings.Index(filename, ".wh.") != -1 {
					deleteflag = true
					filename = strings.Replace(filename, ".wh.", "", -1)
				}
				//
				var status = "A"

				// check add or changes
				var fileHistoryList = fileHistoryListMap[filename]
				var fileHistoryIndex = 0
				var fileSize int64
				fileSize = 0
				for _, fileHistoryID := range fileHistoryList {
					var lTH2Size int64
					lTH2Size = 0
					for _, lTH2 := range layerTarMap[fileHistoryID] {
						if lTH2.Name == layerTarHeader.Name || lTH2.Name == filename {
							lTH2Size = lTH2.Size
							break
						}
					}
					fileSize = lTH2Size - fileSize
					if fileHistoryID == layerID {
						if fileHistoryIndex == 0 {

						} else {
							status = "C"

						}
						break
					}
					fileHistoryIndex++
				}

				if deleteflag {
					status = "D"
				}
				// calc size
				//
				var isOutput = false
				if isFiltering {
					if m, _ := regexp.MatchString(filteringWord, filename); m {
						isOutput = true
					}
				} else {
					isOutput = true
				}
				if isOutput {
					//fmt.Println(status + " " + filename + " " + strconv.FormatInt(fileSize, 10) + " " + strconv.Itoa(layerTarHeader.Uid) + "(" + layerTarHeader.Uname + ")" + ":" + strconv.Itoa(layerTarHeader.Gid) + "(" + layerTarHeader.Gname + ")" + " " + strconv.FormatInt(layerTarHeader.Mode, 8))
					fmt.Println(status + " " + filename + " " + strconv.FormatInt(fileSize, 10) + " " + strconv.Itoa(layerTarHeader.Uid) + "(" + etcpasswduidnamemap[layerTarHeader.Uid] + ")" + ":" + strconv.Itoa(layerTarHeader.Gid) + "(" + etcgroupgidnamemap[layerTarHeader.Gid] + ")" + " " + strconv.FormatInt(layerTarHeader.Mode, 8))
				}
			}

			layerIndex++
		}
	}

}
<commit_msg>prepare 0.1.3<commit_after>package main

import (
	"archive/tar"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/fsouza/go-dockerclient"
)

const version = "0.1.3-dev"

func main() {
	endpoint := "unix:///var/run/docker.sock"
	client, _ := docker.NewClient(endpoint)
	var isFiltering = false
	var filteringWord = ""
	var argc = len(os.Args)
	if argc < 2 {
		os.Exit(2)
	} else if argc == 3 {
		isFiltering = true
		filteringWord = os.Args[2]
	}

	historyList, err := client.ImageHistory(os.Args[1])
	if err != nil {
		fmt.Println("Error happens at client.ImageHistory")
		os.Exit(2)
	}
	// First CreatedBy newer to older
	var createdByHistoryList = []string{}
	//
	var etcpasswdmap = map[string]string{}
	var etcgroupmap = map[string]string{}
	// WORKAROUND
	var etcpasswdstring = ""
	var etcgroupstring = ""
	// WORKAROUND
	var etcpasswduidnamemap = map[int]string{}
	var etcgroupgidnamemap = map[int]string{}
	// layer ID -> json
	var jsonMap = map[string]string{}
	var allJsonMap = map[string]string{}
	var layerTarMap = map[string][]*tar.Header{}
	var first = false
	for _, history := range historyList {
		createdByHistoryList = append(createdByHistoryList, history.CreatedBy)
		image, err := client.InspectImage(history.ID)
		if err != nil || first {
			// continue to create CreatedBy list
			continue
		}
		if image != nil {
			var buf bytes.Buffer
			opts := docker.ExportImageOptions{Name: image.ID, OutputStream: &buf}
			err := client.ExportImage(opts)
			if err != nil {
				fmt.Println("Error happens at client.ExportImage")
				os.Exit(2)
			}
			r := bytes.NewReader(buf.Bytes())
			tr := tar.NewReader(r)
			var header *tar.Header
			for {
				header, err = tr.Next()
				if err == io.EOF {
					// end of image tar
					break
				}
				if err != nil {
					fmt.Println(err)
					fmt.Println("Error at tar extract")
					os.Exit(2)
				}

				buf2 := new(bytes.Buffer)
				if _, err = io.Copy(buf2, tr); err != nil {
					fmt.Println(err)
				}
				//
				var field = strings.Split(header.Name, "/")
				var layerID = field[0]
				// find json and layer.tar
				if strings.HasSuffix(header.Name, "/json") {
					var jsonstring = buf2.String()
					//var imagestring = ""
					allJsonMap[layerID] = jsonstring
					if strings.Index(jsonstring, "\"Image\":\"\"") != -1 || !first {
						jsonMap[layerID] = jsonstring
					}
				} else if strings.HasSuffix(header.Name, "layer.tar") {

					r2 := bytes.NewReader(buf2.Bytes())
					layerTar := tar.NewReader(r2)
					for {
						layerTarHeader, e4 := layerTar.Next()
						if e4 == io.EOF {
							// end of layer.tar
							break
						}
						layerTarMap[layerID] = append(layerTarMap[layerID], layerTarHeader)
						// read for etcpasswd and etcgroup
						if layerTarHeader.Name == "etc/passwd" || layerTarHeader.Name == "etc/group" {
							layerTarBuffer := new(bytes.Buffer)
							if _, err = io.Copy(layerTarBuffer, layerTar); err != nil {
								fmt.Println(err)
							}
							// TODO store json data
							var etcfilejsonstring = layerTarBuffer.String()
							// fmt.Println("FILENAME=[" + layerTarHeader.Name + "]")
							// fmt.Println(etcfilejsonstring)
							if layerTarHeader.Name == "etc/passwd" {
								etcpasswdmap[layerID] = etcfilejsonstring

								if etcpasswdstring == "" {
									etcpasswdstring = etcfilejsonstring
									var lines = strings.Split(etcpasswdstring, "\n")
									for _, line := range lines {
										var fields = strings.Split(line, ":")
										if len(fields) > 2 {
											var uid, _ = strconv.Atoi(fields[2])
											etcpasswduidnamemap[uid] = fields[0]
										}
									}
								}
							} else if layerTarHeader.Name == "etc/group" {
								etcgroupmap[layerID] = etcfilejsonstring
								if etcgroupstring == "" {
									etcgroupstring = etcfilejsonstring
									var lines = strings.Split(etcgroupstring, "\n")
									for _, line := range lines {
										var fields = strings.Split(line, ":")
										if len(fields) > 2 {
											var gid, _ = strconv.Atoi(fields[2])
											etcgroupgidnamemap[gid] = fields[0]
										}
									}
								}
							}
						}
					}
				}
			}
		}
		first = true

	}
	// fmt.Println("---- createdBy")
	// for _, createdBy := range createdByHistoryList {
	// 	fmt.Println(createdBy)
	// }
	// construct parent map
	//fmt.Println("---- jsonMap")
	var relation = map[string]string{}
	//var key string
	var value string
	var noparentid string
	for _, value = range jsonMap {
		//fmt.Println(key + " -> " + value)
		//dec := json.NewDecoder(v)
		var f interface{}
		//dec.Decode(&d)
		json.Unmarshal([]byte(value), &f)
		//fmt.Printf("%+v\n", d)
		m := f.(map[string]interface{})
		var id = m["id"]
		var parent = m["parent"]
		if parent == nil {
			noparentid = id.(string)
		} else {
			var p = parent.(string)
			_, ok := relation[p]
			if !ok {
				relation[p] = id.(string)
			}
		}
	}

	// fmt.Println("---- tarinfo")
	// fmt.Println("Start from:" + noparentid)
	var currentid = noparentid

	for {
		//fmt.Println(currentid)
		// find next
		_, ok := relation[currentid]
		if !ok {
			break
		}
		currentid = relation[currentid]
	}
	// File History List key is filename and value is layerid
	var fileHistoryListMap = map[string][]string{}
	var layerHistoryList = []string{}
	// fmt.Println("---- reconstruct")
	// fmt.Println("Start from:" + noparentid)
	currentid = noparentid
	for {
		//
		for _, layerTarHeader := range layerTarMap[currentid] {
			var filename = layerTarHeader.Name
			filename = strings.Replace(filename, ".wh.", "", -1)
			fileHistoryListMap[filename] = append(fileHistoryListMap[filename], currentid)
		}
		//
		layerHistoryList = append(layerHistoryList, currentid)
		_, ok := relation[currentid]
		if !ok {
			break
		}
		// fmt.Println(currentid + "->" + relation[currentid])
		currentid = relation[currentid]

	}
	//

	//
	// fmt.Println("---- layer")
	// reverse
	// SliceTricks · golang/go Wiki
	// https://github.com/golang/go/wiki/SliceTricks
	for i := len(layerHistoryList)/2 - 1; i >= 0; i-- {
		opp := len(layerHistoryList) - 1 - i
		layerHistoryList[i], layerHistoryList[opp] = layerHistoryList[opp], layerHistoryList[i]
	}
	// for _, layer := range layerHistoryList {
	// 	fmt.Println(layer)
	// }
	// fmt.Println("---- createdBy layer")
	// for _, createdBy := range createdByHistoryList {
	// 	fmt.Println(createdBy)
	// }
	var createdByListMap = map[string][]string{}
	var currentLayerID = ""
	var currentLayerIndex = 0
	for _, createdBy := range createdByHistoryList {
		if strings.Index(createdBy, "#(nop)") != -1 && strings.Index(createdBy, "ADD") == -1 && strings.Index(createdBy, "COPY") == -1 {
			//fmt.Println(strings.Repeat("-", 12) + " " + createdBy)
		} else {
			currentLayerID = layerHistoryList[currentLayerIndex]
			currentLayerIndex++
		}
		createdByListMap[currentLayerID] = append(createdByListMap[currentLayerID], createdBy)
	}
	//
	var layerIndex = 0

	for _, createdBy := range createdByHistoryList {
		if strings.Index(createdBy, "#(nop)") != -1 && strings.Index(createdBy, "ADD") == -1 && strings.Index(createdBy, "COPY") == -1 {
			//fmt.Println(strings.Repeat("-", 12) + " " + createdBy)
		} else {
			var layerID = layerHistoryList[layerIndex]
			for _, savedCreatedBy := range createdByListMap[layerID] {
				fmt.Println(layerID[:12] + "  " + savedCreatedBy)
			}
			//fmt.Println(allJsonMap[layerID])

			for _, layerTarHeader := range layerTarMap[layerID] {
				var filename = layerTarHeader.Name
				var deleteflag = false
				if strings.Index(filename, ".wh.") != -1 {
					deleteflag = true
					filename = strings.Replace(filename, ".wh.", "", -1)
				}
				//
				var status = "A"

				// check add or changes
				var fileHistoryList = fileHistoryListMap[filename]
				var fileHistoryIndex = 0
				var fileSize int64
				fileSize = 0
				for _, fileHistoryID := range fileHistoryList {
					var lTH2Size int64
					lTH2Size = 0
					for _, lTH2 := range layerTarMap[fileHistoryID] {
						if lTH2.Name == layerTarHeader.Name || lTH2.Name == filename {
							lTH2Size = lTH2.Size
							break
						}
					}
					fileSize = lTH2Size - fileSize
					if fileHistoryID == layerID {
						if fileHistoryIndex == 0 {

						} else {
							status = "C"

						}
						break
					}
					fileHistoryIndex++
				}

				if deleteflag {
					status = "D"
				}
				// calc size
				//
				var isOutput = false
				if isFiltering {
					if m, _ := regexp.MatchString(filteringWord, filename); m {
						isOutput = true
					}
				} else {
					isOutput = true
				}
				if isOutput {
					//fmt.Println(status + " " + filename + " " + strconv.FormatInt(fileSize, 10) + " " + strconv.Itoa(layerTarHeader.Uid) + "(" + layerTarHeader.Uname + ")" + ":" + strconv.Itoa(layerTarHeader.Gid) + "(" + layerTarHeader.Gname + ")" + " " + strconv.FormatInt(layerTarHeader.Mode, 8))
					fmt.Println(status + " " + filename + " " + strconv.FormatInt(fileSize, 10) + " " + strconv.Itoa(layerTarHeader.Uid) + "(" + etcpasswduidnamemap[layerTarHeader.Uid] + ")" + ":" + strconv.Itoa(layerTarHeader.Gid) + "(" + etcgroupgidnamemap[layerTarHeader.Gid] + ")" + " " + strconv.FormatInt(layerTarHeader.Mode, 8))
				}
			}

			layerIndex++
		}
	}

}
<|endoftext|>


<commit_before>package shadowsocks

import (
	"context"
	"runtime"
	"time"

	"v2ray.com/core/app"
	"v2ray.com/core/app/dispatcher"
	"v2ray.com/core/app/log"
	"v2ray.com/core/common"
	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/errors"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/protocol"
	"v2ray.com/core/common/signal"
	"v2ray.com/core/proxy"
	"v2ray.com/core/transport/internet"
	"v2ray.com/core/transport/internet/udp"
)

type Server struct {
	config  *ServerConfig
	user    *protocol.User
	account *ShadowsocksAccount
}

func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
	space := app.SpaceFromContext(ctx)
	if space == nil {
		return nil, errors.New("no space in context").Path("Shadowsocks", "Server")
	}
	if config.GetUser() == nil {
		return nil, errors.New("user is not specified").Path("Shadowsocks", "Server")
	}

	rawAccount, err := config.User.GetTypedAccount()
	if err != nil {
		return nil, errors.New("failed to get user account").Base(err).Path("Shadowsocks", "Server")
	}
	account := rawAccount.(*ShadowsocksAccount)

	s := &Server{
		config:  config,
		user:    config.GetUser(),
		account: account,
	}

	return s, nil
}

func (s *Server) Network() net.NetworkList {
	list := net.NetworkList{
		Network: []net.Network{net.Network_TCP},
	}
	if s.config.UdpEnabled {
		list.Network = append(list.Network, net.Network_UDP)
	}
	return list
}

func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher dispatcher.Interface) error {
	switch network {
	case net.Network_TCP:
		return s.handleConnection(ctx, conn, dispatcher)
	case net.Network_UDP:
		return s.handlerUDPPayload(ctx, conn, dispatcher)
	default:
		return errors.New("unknown network: ", network).Path("Shadowsocks", "Server")
	}
}

func (v *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection, dispatcher dispatcher.Interface) error {
	udpServer := udp.NewDispatcher(dispatcher)

	reader := buf.NewReader(conn)
	for {
		payload, err := reader.Read()
		if err != nil {
			break
		}

		request, data, err := DecodeUDPPacket(v.user, payload)
		if err != nil {
			if source, ok := proxy.SourceFromContext(ctx); ok {
				log.Trace(errors.New("dropping invalid UDP packet from: ", source).Base(err).Path("Shadowsocks", "Server"))
				log.Access(source, "", log.AccessRejected, err)
			}
			payload.Release()
			continue
		}

		if request.Option.Has(RequestOptionOneTimeAuth) && v.account.OneTimeAuth == Account_Disabled {
			log.Trace(errors.New("client payload enables OTA but server doesn't allow it").Path("Shadowsocks", "Server"))
			payload.Release()
			continue
		}

		if !request.Option.Has(RequestOptionOneTimeAuth) && v.account.OneTimeAuth == Account_Enabled {
			log.Trace(errors.New("client payload disables OTA but server forces it").Path("Shadowsocks", "Server"))
			payload.Release()
			continue
		}

		dest := request.Destination()
		if source, ok := proxy.SourceFromContext(ctx); ok {
			log.Access(source, dest, log.AccessAccepted, "")
		}
		log.Trace(errors.New("tunnelling request to ", dest).Path("Shadowsocks", "Server"))

		ctx = protocol.ContextWithUser(ctx, request.User)
		udpServer.Dispatch(ctx, dest, data, func(payload *buf.Buffer) {
			defer payload.Release()

			data, err := EncodeUDPPacket(request, payload)
			if err != nil {
				log.Trace(errors.New("failed to encode UDP packet").Base(err).Path("Shadowsocks", "Server").AtWarning())
				return
			}
			defer data.Release()

			conn.Write(data.Bytes())
		})
	}

	return nil
}

func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, dispatcher dispatcher.Interface) error {
	conn.SetReadDeadline(time.Now().Add(time.Second * 8))
	bufferedReader := buf.NewBufferedReader(conn)
	request, bodyReader, err := ReadTCPSession(s.user, bufferedReader)
	if err != nil {
		log.Access(conn.RemoteAddr(), "", log.AccessRejected, err)
		return errors.New("failed to create request from: ", conn.RemoteAddr()).Base(err).Path("Shadowsocks", "Server")
	}
	conn.SetReadDeadline(time.Time{})

	bufferedReader.SetBuffered(false)

	dest := request.Destination()
	log.Access(conn.RemoteAddr(), dest, log.AccessAccepted, "")
	log.Trace(errors.New("tunnelling request to ", dest).Path("Shadowsocks", "Server"))

	ctx = protocol.ContextWithUser(ctx, request.User)

	userSettings := s.user.GetSettings()
	ctx, timer := signal.CancelAfterInactivity(ctx, userSettings.PayloadTimeout)
	ray, err := dispatcher.Dispatch(ctx, dest)
	if err != nil {
		return err
	}

	responseDone := signal.ExecuteAsync(func() error {
		bufferedWriter := buf.NewBufferedWriter(conn)
		responseWriter, err := WriteTCPResponse(request, bufferedWriter)
		if err != nil {
			return errors.New("failed to write response").Base(err).Path("Shadowsocks", "Server")
		}

		mergeReader := buf.NewMergingReader(ray.InboundOutput())
		payload, err := mergeReader.Read()
		if err != nil {
			return err
		}
		if err := responseWriter.Write(payload); err != nil {
			return err
		}
		payload.Release()

		if err := bufferedWriter.SetBuffered(false); err != nil {
			return err
		}

		if err := buf.PipeUntilEOF(timer, mergeReader, responseWriter); err != nil {
			return errors.New("failed to transport all TCP response").Base(err).Path("Shadowsocks", "Server")
		}

		return nil
	})

	requestDone := signal.ExecuteAsync(func() error {
		defer ray.InboundInput().Close()

		if err := buf.PipeUntilEOF(timer, bodyReader, ray.InboundInput()); err != nil {
			return errors.New("failed to transport all TCP request").Base(err).Path("Shadowsocks", "Server")
		}
		return nil
	})

	if err := signal.ErrorOrFinish2(ctx, requestDone, responseDone); err != nil {
		ray.InboundInput().CloseError()
		ray.InboundOutput().CloseError()
		return errors.New("connection ends").Base(err).Path("Shadowsocks", "Server")
	}

	runtime.KeepAlive(timer)

	return nil
}

func init() {
	common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
		return NewServer(ctx, config.(*ServerConfig))
	}))
}
<commit_msg>update error messages<commit_after>package shadowsocks

import (
	"context"
	"runtime"
	"time"

	"v2ray.com/core/app"
	"v2ray.com/core/app/dispatcher"
	"v2ray.com/core/app/log"
	"v2ray.com/core/common"
	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/errors"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/protocol"
	"v2ray.com/core/common/signal"
	"v2ray.com/core/proxy"
	"v2ray.com/core/transport/internet"
	"v2ray.com/core/transport/internet/udp"
)

type Server struct {
	config  *ServerConfig
	user    *protocol.User
	account *ShadowsocksAccount
}

// NewServer create a new Shadowsocks server.
func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
	space := app.SpaceFromContext(ctx)
	if space == nil {
		return nil, errors.New("no space in context").Path("Proxy", "Shadowsocks", "Server")
	}
	if config.GetUser() == nil {
		return nil, errors.New("user is not specified").Path("Proxy", "Shadowsocks", "Server")
	}

	rawAccount, err := config.User.GetTypedAccount()
	if err != nil {
		return nil, errors.New("failed to get user account").Base(err).Path("Proxy", "Shadowsocks", "Server")
	}
	account := rawAccount.(*ShadowsocksAccount)

	s := &Server{
		config:  config,
		user:    config.GetUser(),
		account: account,
	}

	return s, nil
}

func (s *Server) Network() net.NetworkList {
	list := net.NetworkList{
		Network: []net.Network{net.Network_TCP},
	}
	if s.config.UdpEnabled {
		list.Network = append(list.Network, net.Network_UDP)
	}
	return list
}

func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher dispatcher.Interface) error {
	switch network {
	case net.Network_TCP:
		return s.handleConnection(ctx, conn, dispatcher)
	case net.Network_UDP:
		return s.handlerUDPPayload(ctx, conn, dispatcher)
	default:
		return errors.New("unknown network: ", network).Path("Proxy", "Shadowsocks", "Server")
	}
}

func (v *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection, dispatcher dispatcher.Interface) error {
	udpServer := udp.NewDispatcher(dispatcher)

	reader := buf.NewReader(conn)
	for {
		payload, err := reader.Read()
		if err != nil {
			break
		}

		request, data, err := DecodeUDPPacket(v.user, payload)
		if err != nil {
			if source, ok := proxy.SourceFromContext(ctx); ok {
				log.Trace(errors.New("dropping invalid UDP packet from: ", source).Base(err).Path("Proxy", "Shadowsocks", "Server"))
				log.Access(source, "", log.AccessRejected, err)
			}
			payload.Release()
			continue
		}

		if request.Option.Has(RequestOptionOneTimeAuth) && v.account.OneTimeAuth == Account_Disabled {
			log.Trace(errors.New("client payload enables OTA but server doesn't allow it").Path("Proxy", "Shadowsocks", "Server"))
			payload.Release()
			continue
		}

		if !request.Option.Has(RequestOptionOneTimeAuth) && v.account.OneTimeAuth == Account_Enabled {
			log.Trace(errors.New("client payload disables OTA but server forces it").Path("Proxy", "Shadowsocks", "Server"))
			payload.Release()
			continue
		}

		dest := request.Destination()
		if source, ok := proxy.SourceFromContext(ctx); ok {
			log.Access(source, dest, log.AccessAccepted, "")
		}
		log.Trace(errors.New("tunnelling request to ", dest).Path("Proxy", "Shadowsocks", "Server"))

		ctx = protocol.ContextWithUser(ctx, request.User)
		udpServer.Dispatch(ctx, dest, data, func(payload *buf.Buffer) {
			defer payload.Release()

			data, err := EncodeUDPPacket(request, payload)
			if err != nil {
				log.Trace(errors.New("failed to encode UDP packet").Base(err).Path("Proxy", "Shadowsocks", "Server").AtWarning())
				return
			}
			defer data.Release()

			conn.Write(data.Bytes())
		})
	}

	return nil
}

func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, dispatcher dispatcher.Interface) error {
	conn.SetReadDeadline(time.Now().Add(time.Second * 8))
	bufferedReader := buf.NewBufferedReader(conn)
	request, bodyReader, err := ReadTCPSession(s.user, bufferedReader)
	if err != nil {
		log.Access(conn.RemoteAddr(), "", log.AccessRejected, err)
		return errors.New("failed to create request from: ", conn.RemoteAddr()).Base(err).Path("Proxy", "Shadowsocks", "Server")
	}
	conn.SetReadDeadline(time.Time{})

	bufferedReader.SetBuffered(false)

	dest := request.Destination()
	log.Access(conn.RemoteAddr(), dest, log.AccessAccepted, "")
	log.Trace(errors.New("tunnelling request to ", dest).Path("Proxy", "Shadowsocks", "Server"))

	ctx = protocol.ContextWithUser(ctx, request.User)

	userSettings := s.user.GetSettings()
	ctx, timer := signal.CancelAfterInactivity(ctx, userSettings.PayloadTimeout)
	ray, err := dispatcher.Dispatch(ctx, dest)
	if err != nil {
		return err
	}

	responseDone := signal.ExecuteAsync(func() error {
		bufferedWriter := buf.NewBufferedWriter(conn)
		responseWriter, err := WriteTCPResponse(request, bufferedWriter)
		if err != nil {
			return errors.New("failed to write response").Base(err).Path("Proxy", "Shadowsocks", "Server")
		}

		mergeReader := buf.NewMergingReader(ray.InboundOutput())
		payload, err := mergeReader.Read()
		if err != nil {
			return err
		}
		if err := responseWriter.Write(payload); err != nil {
			return err
		}
		payload.Release()

		if err := bufferedWriter.SetBuffered(false); err != nil {
			return err
		}

		if err := buf.PipeUntilEOF(timer, mergeReader, responseWriter); err != nil {
			return errors.New("failed to transport all TCP response").Base(err).Path("Proxy", "Shadowsocks", "Server")
		}

		return nil
	})

	requestDone := signal.ExecuteAsync(func() error {
		defer ray.InboundInput().Close()

		if err := buf.PipeUntilEOF(timer, bodyReader, ray.InboundInput()); err != nil {
			return errors.New("failed to transport all TCP request").Base(err).Path("Proxy", "Shadowsocks", "Server")
		}
		return nil
	})

	if err := signal.ErrorOrFinish2(ctx, requestDone, responseDone); err != nil {
		ray.InboundInput().CloseError()
		ray.InboundOutput().CloseError()
		return errors.New("connection ends").Base(err).Path("Proxy", "Shadowsocks", "Server")
	}

	runtime.KeepAlive(timer)

	return nil
}

func init() {
	common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
		return NewServer(ctx, config.(*ServerConfig))
	}))
}
<|endoftext|>


<commit_before>package main

import (
    "github.com/hoisie/web.go"
    "kview"
    "os"
    "time"
    "log"
)

var home_view, edit_view kview.View

func viewInit() {
    layout := kview.New("layout.kt")
    menu   := kview.New("menu.kt")

    // Create right column
    right  := kview.New("right.kt")
    // Add view components
    right.Div("Info",       kview.New("right/info.kt"))
    right.Div("Commercial", kview.New("right/commercial.kt"))

    // Create home view as layout copy.
    home_view = layout.Copy()
    home_view.Div("Menu",  menu)
    home_view.Div("Left",  kview.New("left/home.kt"))
    home_view.Div("Right", right)

    // Create edit view.
    edit_view = layout.Copy()
    edit_view.Div("Menu",  menu)
    edit_view.Div("Left",  kview.New("left/edit.kt"))
    edit_view.Div("Right", right)
}

type MenuItem struct {
    name, url string
}

type Menu struct {
    content  []MenuItem
    selected int
}

type RightCtx struct {
    commercial string
}

type Ctx struct {
    menu  Menu
    left  interface{}
    right RightCtx
}

type MainCtx struct {
    title string
    ctx   Ctx
}

var (
    menu = []MenuItem {
        MenuItem{"Home", "/"},
        MenuItem{"Edit", "/edit"},
    }
    global_ctx = struct{started, last_cli_addr string; hits uint} {
        time.LocalTime().Format("2006-01-02 15:04"),
        "",
        0,
    }
)

func exec(web_ctx *web.Context, view kview.View, req_ctx interface{}) {
    global_ctx.hits++
    view.Exec(web_ctx, global_ctx, req_ctx)
    global_ctx.last_cli_addr = web_ctx.RemoteAddr
}

func home(web_ctx *web.Context) {
    req_ctx := MainCtx {
        title: "Home page",
        ctx:   Ctx {
            menu: Menu{menu, 0},
            left: []string {
                "This is a test service created entirely in Go (golang) " +
                "using kasia.go, kview and web.go " +
                "packages.",
                "Please select another menu item!",
            },
            right: RightCtx{"A house is much better than a flat. " +
                "So buy a new House today!"},
        },
    }
    exec(web_ctx, home_view, req_ctx)
}

func edit(web_ctx *web.Context) {
    req_ctx := MainCtx {
        title : "Edit page",
        ctx   : Ctx {
            menu:  Menu{menu, 1},
            left:  []string {
                "Hello! You can modify this example.",
                "Open simple.go file or some template file in your " +
                "editor and edit it.",
                "Then type: $ make && ./simple",
            },
            right: RightCtx{
                "To modify this example you may download " +
                "tabby source " +
                "editor writen entirely in Go!",
            },
        },
    }
    exec(web_ctx, edit_view, req_ctx)
}

// Init and run

func main() {
    if len(os.Args) == 3 {
        chrootuid(os.Args[1], os.Args[2])
    } else if len(os.Args) != 1 {
        log.Exitf("Usage: %s [DIRECTORY UID]\n", os.Args[0])
    }

    // Change kview default template directory and error handler
    //kview.TemplatesDir = "some_dir"
    //kview.ErrorHandler = new_error_handler

    viewInit()
    web.Get("/", home)
    web.Get("/edit", edit)
    web.Config.StaticDir = "static"
    web.Run("0.0.0.0:9999")
}
<commit_msg>Set goinstal as preferred command for installation<commit_after>package main

import (
    "os"
    "time"
    "log"
    "github.com/hoisie/web.go"
    "github.com/ziutek/kview"
)

var home_view, edit_view kview.View

func viewInit() {
    layout := kview.New("layout.kt")
    menu   := kview.New("menu.kt")

    // Create right column
    right  := kview.New("right.kt")
    // Add view components
    right.Div("Info",       kview.New("right/info.kt"))
    right.Div("Commercial", kview.New("right/commercial.kt"))

    // Create home view as layout copy.
    home_view = layout.Copy()
    home_view.Div("Menu",  menu)
    home_view.Div("Left",  kview.New("left/home.kt"))
    home_view.Div("Right", right)

    // Create edit view.
    edit_view = layout.Copy()
    edit_view.Div("Menu",  menu)
    edit_view.Div("Left",  kview.New("left/edit.kt"))
    edit_view.Div("Right", right)
}

type MenuItem struct {
    name, url string
}

type Menu struct {
    content  []MenuItem
    selected int
}

type RightCtx struct {
    commercial string
}

type Ctx struct {
    menu  Menu
    left  interface{}
    right RightCtx
}

type MainCtx struct {
    title string
    ctx   Ctx
}

var (
    menu = []MenuItem {
        MenuItem{"Home", "/"},
        MenuItem{"Edit", "/edit"},
    }
    global_ctx = struct{started, last_cli_addr string; hits uint} {
        time.LocalTime().Format("2006-01-02 15:04"),
        "",
        0,
    }
)

func exec(web_ctx *web.Context, view kview.View, req_ctx interface{}) {
    global_ctx.hits++
    view.Exec(web_ctx, global_ctx, req_ctx)
    global_ctx.last_cli_addr = web_ctx.RemoteAddr
}

func home(web_ctx *web.Context) {
    req_ctx := MainCtx {
        title: "Home page",
        ctx:   Ctx {
            menu: Menu{menu, 0},
            left: []string {
                "This is a test service created entirely in Go (golang) " +
                "using kasia.go, kview and web.go " +
                "packages.",
                "Please select another menu item!",
            },
            right: RightCtx{"A house is much better than a flat. " +
                "So buy a new House today!"},
        },
    }
    exec(web_ctx, home_view, req_ctx)
}

func edit(web_ctx *web.Context) {
    req_ctx := MainCtx {
        title : "Edit page",
        ctx   : Ctx {
            menu:  Menu{menu, 1},
            left:  []string {
                "Hello! You can modify this example.",
                "Open simple.go file or some template file in your " +
                "editor and edit it.",
                "Then type: $ make && ./simple",
            },
            right: RightCtx{
                "To modify this example you may download " +
                "tabby source " +
                "editor writen entirely in Go!",
            },
        },
    }
    exec(web_ctx, edit_view, req_ctx)
}

// Init and run

func main() {
    if len(os.Args) == 3 {
        chrootuid(os.Args[1], os.Args[2])
    } else if len(os.Args) != 1 {
        log.Exitf("Usage: %s [DIRECTORY UID]\n", os.Args[0])
    }

    // Change kview default template directory and error handler
    //kview.TemplatesDir = "some_dir"
    //kview.ErrorHandler = new_error_handler

    viewInit()
    web.Get("/", home)
    web.Get("/edit", edit)
    web.Config.StaticDir = "static"
    web.Run("0.0.0.0:9999")
}
<|endoftext|>


<commit_before>package gdrj

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/eaciit/dbox"
	"github.com/eaciit/orm/v1"
	"github.com/eaciit/toolkit"
)

type LedgerSummary struct {
	orm.ModelBase                          `bson:"-" json:"-"`
	ID                                     string `bson:"_id"`
	PC                                     *ProfitCenter
	CC                                     *CostCenter
	CompanyCode                            string
	LedgerAccount                          string
	Customer                               *Customer
	Product                                *Product
	Date                                   *Date
	PLGroup1, PLGroup2, PLGroup3, PLGroup4 string
	Value1, Value2, Value3                 float64
}

func (s *LedgerSummary) RecordID() interface{} {
	return s.ID
	//return toolkit.Sprintf("%d_%d_%s_%s", s.Date.Year, s.Date.Month, s.CompanyCode, s.LedgerAccount)
}

func (s *LedgerSummary) PrepareID() interface{} {
	s.ID = toolkit.Sprintf("%d_%d_%s_%s", s.Date.Year, s.Date.Month, s.CompanyCode, s.LedgerAccount)
	return s
}

func (s *LedgerSummary) TableName() string {
	return "ledgersummaries"
}

func SummaryGenerateDummyData() []*LedgerSummary {
	res := []*LedgerSummary{}
	pcs := []*ProfitCenter{}
	ccs := []*CostCenter{}
	cus := []*Customer{}
	prs := []*Product{}
	das := []*Date{}

	for i := 0; i < 5; i++ {
		pc := new(ProfitCenter)
		pc.ID = fmt.Sprintf("PC00%d", i)
		pc.EntityID = toolkit.RandomString(5)
		pc.Name = toolkit.RandomString(10)
		pc.BrandID = toolkit.RandomString(5)
		pc.BrandCategoryID = toolkit.RandomString(5)
		pc.BranchID = toolkit.RandomString(5)
		pc.BranchType = BranchTypeEnum(toolkit.RandInt(100))
		pcs = append(pcs, pc)

		cc := new(CostCenter)
		cc.ID = fmt.Sprintf("CC00%d", i)
		cc.EntityID = toolkit.RandomString(5)
		cc.Name = toolkit.RandomString(10)
		cc.CostGroup01 = toolkit.RandomString(5)
		cc.CostGroup02 = toolkit.RandomString(5)
		cc.CostGroup03 = toolkit.RandomString(5)
		cc.BranchID = toolkit.RandomString(5)
		cc.BranchType = BranchTypeEnum(toolkit.RandInt(100))
		cc.CCTypeID = toolkit.RandomString(5)
		cc.HCCGroupID = toolkit.RandomString(5)
		ccs = append(ccs, cc)

		cu := new(Customer)
		cu.ID = toolkit.RandomString(5)
		cu.BranchName = toolkit.RandomString(5)
		cu.BranchID = toolkit.RandomString(5)
		cu.Name = toolkit.RandomString(5)
		cu.KeyAccount = toolkit.RandomString(5)
		cu.ChannelName = toolkit.RandomString(5)
		cu.CustomerGroupName = toolkit.RandomString(5)
		cu.National = toolkit.RandomString(5)
		cu.Zone = toolkit.RandomString(5)
		cu.Region = toolkit.RandomString(5)
		cu.Area = toolkit.RandomString(5)
		cus = append(cus, cu)

		pr := new(Product)
		pr.ID = toolkit.RandomString(5)
		pr.Name = toolkit.RandomString(5)
		pr.Brand = toolkit.RandomString(5)
		prs = append(prs, pr)

		da := new(Date)
		da.ID = toolkit.RandomString(5)
		da.Date = time.Now()
		da.Month = time.Month(5)
		da.Quarter = toolkit.RandInt(100)
		da.Year = toolkit.RandInt(100)
		das = append(das, da)
	}

	for i := 0; i < 100; i++ {
		o := new(LedgerSummary)
		o.ID = fmt.Sprintf("LS00%d", i)
		o.PC = pcs[i%len(pcs)]
		o.CC = ccs[i%len(ccs)]
		o.CompanyCode = toolkit.RandomString(3)
		o.LedgerAccount = toolkit.RandomString(3)
		o.Customer = cus[i%len(cus)]
		o.Product = prs[i%len(prs)]
		o.Date = das[i%len(das)]
		o.Value1 = toolkit.RandFloat(3000, 2)
		o.Value2 = toolkit.RandFloat(3000, 2)
		o.Value3 = toolkit.RandFloat(3000, 2)
		o.Save()

		res = append(res, o)
	}

	return res
}

/*
[
    {_id:{col1:"D1",col2:"D2",col3:"D3"},SalesAmount:10,Qty:5,Value:2},
    {_id:{col1:"D1",col2:"D2",col3:"D4"},SalesAmount:10,Qty:3.2,Value:3},
]
row: _id.col1, _id.col2
col: _id.col3
*/
func SummarizeLedgerSum(
	filter *dbox.Filter,
	columns []string,
	datapoints []string,
	// misal: ["sum:Value1:SalesAmount","sum:Value2:Qty","avg:Value3"]
	fnTransform func(m *toolkit.M) error) ([]toolkit.M, error) {
	sum := new(LedgerSummary)
	conn := DB().Connection
	q := conn.NewQuery().From(sum.TableName())
	if filter != nil {
		q = q.Where(filter)
	}
	if len(columns) > 0 {
		cs := []string{}
		for i := range columns {
			cs = append(cs, strings.ToLower(columns[i]))
		}

		q = q.Group(cs...)
	}
	if len(datapoints) == 0 {
		return nil, errors.New("SummarizedLedgerSum: Datapoints should be defined at least 1")
	}
	for _, dp := range datapoints {
		dps := strings.Split(strings.ToLower(dp), ":")
		if len(dps) < 2 {
			return nil, errors.New("SummarizeLedgerSum: Parameters should follow this pattern aggrOp:fieldName:[alias - optional]")
		}

		fieldid := dps[1]
		alias := fieldid
		op := ""
		if !strings.HasPrefix(dps[0], "$") {
			dps[0] = "$" + strings.ToLower(dps[0])
		}

		if toolkit.HasMember([]string{dbox.AggrSum, dbox.AggrAvr, dbox.AggrMax,
			dbox.AggrMin, dbox.AggrMean, dbox.AggrMed}, dps[0]) {
			op = dps[0]
		}
		if op == "" {
			return nil, errors.New("SummarizeLedgerSum: Invalid Operation")
		}
		if len(dps) > 2 {
			alias = dps[2]
		}

		if strings.HasPrefix(alias, "$") {
			alias = alias[1:]
		}

		if fnumber, enumber := toolkit.IsStringNumber(fieldid, "."); enumber == nil {
			q = q.Aggr(op, fnumber, alias)
		} else {
			q = q.Aggr(op, fieldid, alias)
		}
	}

	c, e := q.Cursor(nil)
	if e != nil {
		return nil, errors.New("SummarizedLedgerSum: Preparing cursor error " + e.Error())
	}
	defer c.Close()

	ms := []toolkit.M{}
	e = c.Fetch(&ms, 0, false)
	if e != nil {
		return nil, errors.New("SummarizedLedgerSum: Fetch cursor error " + e.Error())
	}

	if c.Count() > 0 {
		e = c.Fetch(&ms, 0, false)
		if e != nil {
			return nil, errors.New("SummarizedLedgerSum: Fetch cursor error " + e.Error())
		}
	}

	if fnTransform != nil {
		for idx, m := range ms {
			e = fnTransform(&m)
			if e != nil {
				return nil, errors.New(toolkit.Sprintf("SummarizedLedgerSum: Transform error on index %d, %s",
					idx, e.Error()))
			}
		}
	}

	return ms, nil
}

func (s *LedgerSummary) Save() error {
	e := Save(s)
	if e != nil {
		return errors.New(toolkit.Sprintf("[%v-%v] Error found : ", s.TableName(), "save", e.Error()))
	}
	return e
}

type PivotParam struct {
	Dimensions []*PivotParamDimensions `json:"dimensions"`
	DataPoints []*PivotParamDataPoint  `json:"datapoints"`
}

type PivotParamDimensions struct {
	Field string `json:"field"`
	Type  string `json:"type"`
	Alias string `json:"alias"`
}

type PivotParamDataPoint struct {
	OP    string `json:"op"`
	Field string `json:"field"`
	Alias string `json:"alias"`
}

func (p *PivotParam) ParseDimensions() (res []string) {
	res = []string{}
	for _, each := range p.Dimensions {
		res = append(res, each.Field)
	}
	return
}

func (p *PivotParam) ParseDataPoints() (res []string) {
	for _, each := range p.DataPoints {
		parts := []string{each.OP, each.Field, each.Alias}

		if !strings.HasPrefix(parts[1], "$") {
			parts[1] = fmt.Sprintf("$%s", parts[1])
		}

		res = append(res, strings.Join(parts, ":"))
	}
	return
}

func (p *PivotParam) MapSummarizedLedger(data []toolkit.M) []toolkit.M {
	res := []toolkit.M{}
	metadata := map[string]string{}

	for i, each := range data {
		row := toolkit.M{}

		if i == 0 {
			// cache the metadata, only on first loop
			for key, val := range each {
				if key == "_id" {
					for key2 := range val.(toolkit.M) {
						keyv := key2

						for _, dimension := range p.Dimensions {
							if strings.ToLower(dimension.Field) == strings.ToLower(keyv) {
								keyv = strings.Replace(strings.Replace(dimension.Field, ".", "", -1), "_id", "_ID", -1)
							}
						}

						if key2 == "_id" {
							keyv = toolkit.TrimByString(keyv, "_")
						}

						metadata[fmt.Sprintf("%s.%s", key, key2)] = keyv
					}
				} else {
					keyv := key
					for _, each := range p.DataPoints {
						if strings.ToLower(each.Alias) == strings.ToLower(key) {
							keyv = strings.Replace(each.Alias, " ", "_", -1)
						}
					}
					metadata[key] = keyv
				}
			}
		}

		// flatten the data
		for key, val := range each {
			if key == "_id" {
				for key2, val2 := range val.(toolkit.M) {
					keyv := metadata[fmt.Sprintf("%s.%s", key, key2)]
					row.Set(keyv, val2)
				}
			} else {
				keyv := metadata[key]
				row.Set(keyv, val)
			}
		}

		res = append(res, row)
	}

	return res
}

func (p *PivotParam) GetPivotConfig(data []toolkit.M) toolkit.M {
	res := struct {
		SchemaModelFields   toolkit.M
		SchemaCubeDimension toolkit.M
		SchemaCubeMeasures  toolkit.M
		Columns             []toolkit.M
		Rows                []toolkit.M
		Measures            []string
	}{
		toolkit.M{},
		toolkit.M{},
		toolkit.M{},
		[]toolkit.M{},
		[]toolkit.M{},
		[]string{},
	}

	if len(data) > 0 {
		for key := range data[0] {
			for _, c := range p.Dimensions {
				a := strings.ToLower(strings.Replace(c.Field, ".", "", -1)) == strings.ToLower(key)
				b := strings.ToLower(toolkit.TrimByString(c.Field, "_")) == strings.ToLower(key)

				if a || b {
					if c.Type == "column" {
						res.Columns = append(res.Columns, toolkit.M{"name": key, "expand": false})
					} else {
						res.Rows = append(res.Rows, toolkit.M{"name": key, "expand": false})
					}

					caption := fmt.Sprintf("All %s", c.Alias)
					res.SchemaModelFields.Set(key, toolkit.M{"type": "string"})
					res.SchemaCubeDimension.Set(key, toolkit.M{"caption": caption})
				}
			}

			for _, c := range p.DataPoints {
				if strings.ToLower(strings.Replace(c.Alias, " ", "_", -1)) == strings.ToLower(key) {
					op := c.OP
					if op == "avg" {
						op = "average"
					}

					res.SchemaModelFields.Set(key, toolkit.M{"type": "number"})
					res.SchemaCubeMeasures.Set(key, toolkit.M{"field": key, "aggregate": op})
					res.Measures = append(res.Measures, key)
				}
			}
		}
	}

	resM, err := toolkit.ToM(res)
	if err != nil {
		fmt.Println(err.Error())
	}

	return resM
}
<commit_msg>some changes<commit_after>package gdrj

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/eaciit/dbox"
	"github.com/eaciit/orm/v1"
	"github.com/eaciit/toolkit"
)

type LedgerSummary struct {
	orm.ModelBase                          `bson:"-" json:"-"`
	ID                                     string `bson:"_id"`
	PC                                     *ProfitCenter
	CC                                     *CostCenter
	CompanyCode                            string
	LedgerAccount                          string
	Customer                               *Customer
	Product                                *Product
	Date                                   *Date
	PLGroup1, PLGroup2, PLGroup3, PLGroup4 string
	Value1, Value2, Value3                 float64
	//EasyForSelect
	PCID, CCID, OutletID, SKUID, PLCode, PLOrder string
	Month                                        time.Month
	Year                                         int
}

// month,year
func (s *LedgerSummary) RecordID() interface{} {
	return s.ID
	//return toolkit.Sprintf("%d_%d_%s_%s", s.Date.Year, s.Date.Month, s.CompanyCode, s.LedgerAccount)
}

func (s *LedgerSummary) PrepareID() interface{} {
	s.ID = toolkit.Sprintf("%d_%d_%s_%s", s.Date.Year, s.Date.Month, s.CompanyCode, s.LedgerAccount)
	return s
}

func (s *LedgerSummary) TableName() string {
	return "ledgersummaries"
}

func GetLedgerSummaryByDetail(LedgerAccount, PCID, CCID, OutletID, SKUID string, Year int, Month time.Month) (ls *LedgerSummary) {
	ls = new(LedgerSummary)

	filter := dbox.And(dbox.Eq("month", Month),
		dbox.Eq("year", Year),
		dbox.Eq("ledgeraccount", LedgerAccount),
		dbox.Contains("pcid", PCID),
		dbox.Contains("ccid", CCID),
		dbox.Contains("outletid", OutletID),
		dbox.Contains("skuid", SKUID))

	cr, err := Find(ls, filter, nil)
	if err != nil {
		return
	}

	_ = cr.Fetch(&ls, 1, false)
	cr.Close()

	return
}

func SummaryGenerateDummyData() []*LedgerSummary {
	res := []*LedgerSummary{}
	pcs := []*ProfitCenter{}
	ccs := []*CostCenter{}
	cus := []*Customer{}
	prs := []*Product{}
	das := []*Date{}

	for i := 0; i < 5; i++ {
		pc := new(ProfitCenter)
		pc.ID = fmt.Sprintf("PC00%d", i)
		pc.EntityID = toolkit.RandomString(5)
		pc.Name = toolkit.RandomString(10)
		pc.BrandID = toolkit.RandomString(5)
		pc.BrandCategoryID = toolkit.RandomString(5)
		pc.BranchID = toolkit.RandomString(5)
		pc.BranchType = BranchTypeEnum(toolkit.RandInt(100))
		pcs = append(pcs, pc)

		cc := new(CostCenter)
		cc.ID = fmt.Sprintf("CC00%d", i)
		cc.EntityID = toolkit.RandomString(5)
		cc.Name = toolkit.RandomString(10)
		cc.CostGroup01 = toolkit.RandomString(5)
		cc.CostGroup02 = toolkit.RandomString(5)
		cc.CostGroup03 = toolkit.RandomString(5)
		cc.BranchID = toolkit.RandomString(5)
		cc.BranchType = BranchTypeEnum(toolkit.RandInt(100))
		cc.CCTypeID = toolkit.RandomString(5)
		cc.HCCGroupID = toolkit.RandomString(5)
		ccs = append(ccs, cc)

		cu := new(Customer)
		cu.ID = toolkit.RandomString(5)
		cu.BranchName = toolkit.RandomString(5)
		cu.BranchID = toolkit.RandomString(5)
		cu.Name = toolkit.RandomString(5)
		cu.KeyAccount = toolkit.RandomString(5)
		cu.ChannelName = toolkit.RandomString(5)
		cu.CustomerGroupName = toolkit.RandomString(5)
		cu.National = toolkit.RandomString(5)
		cu.Zone = toolkit.RandomString(5)
		cu.Region = toolkit.RandomString(5)
		cu.Area = toolkit.RandomString(5)
		cus = append(cus, cu)

		pr := new(Product)
		pr.ID = toolkit.RandomString(5)
		pr.Name = toolkit.RandomString(5)
		pr.Brand = toolkit.RandomString(5)
		prs = append(prs, pr)

		da := new(Date)
		da.ID = toolkit.RandomString(5)
		da.Date = time.Now()
		da.Month = time.Month(5)
		da.Quarter = toolkit.RandInt(100)
		da.Year = toolkit.RandInt(100)
		das = append(das, da)
	}

	for i := 0; i < 100; i++ {
		o := new(LedgerSummary)
		o.ID = fmt.Sprintf("LS00%d", i)
		o.PC = pcs[i%len(pcs)]
		o.CC = ccs[i%len(ccs)]
		o.CompanyCode = toolkit.RandomString(3)
		o.LedgerAccount = toolkit.RandomString(3)
		o.Customer = cus[i%len(cus)]
		o.Product = prs[i%len(prs)]
		o.Date = das[i%len(das)]
		o.Value1 = toolkit.RandFloat(3000, 2)
		o.Value2 = toolkit.RandFloat(3000, 2)
		o.Value3 = toolkit.RandFloat(3000, 2)
		o.Save()

		res = append(res, o)
	}

	return res
}

/*
[
    {_id:{col1:"D1",col2:"D2",col3:"D3"},SalesAmount:10,Qty:5,Value:2},
    {_id:{col1:"D1",col2:"D2",col3:"D4"},SalesAmount:10,Qty:3.2,Value:3},
]
row: _id.col1, _id.col2
col: _id.col3
*/
func SummarizeLedgerSum(
	filter *dbox.Filter,
	columns []string,
	datapoints []string,
	// misal: ["sum:Value1:SalesAmount","sum:Value2:Qty","avg:Value3"]
	fnTransform func(m *toolkit.M) error) ([]toolkit.M, error) {
	sum := new(LedgerSummary)
	conn := DB().Connection
	q := conn.NewQuery().From(sum.TableName())
	if filter != nil {
		q = q.Where(filter)
	}
	if len(columns) > 0 {
		cs := []string{}
		for i := range columns {
			cs = append(cs, strings.ToLower(columns[i]))
		}

		q = q.Group(cs...)
	}
	if len(datapoints) == 0 {
		return nil, errors.New("SummarizedLedgerSum: Datapoints should be defined at least 1")
	}
	for _, dp := range datapoints {
		dps := strings.Split(strings.ToLower(dp), ":")
		if len(dps) < 2 {
			return nil, errors.New("SummarizeLedgerSum: Parameters should follow this pattern aggrOp:fieldName:[alias - optional]")
		}

		fieldid := dps[1]
		alias := fieldid
		op := ""
		if !strings.HasPrefix(dps[0], "$") {
			dps[0] = "$" + strings.ToLower(dps[0])
		}

		if toolkit.HasMember([]string{dbox.AggrSum, dbox.AggrAvr, dbox.AggrMax,
			dbox.AggrMin, dbox.AggrMean, dbox.AggrMed}, dps[0]) {
			op = dps[0]
		}
		if op == "" {
			return nil, errors.New("SummarizeLedgerSum: Invalid Operation")
		}
		if len(dps) > 2 {
			alias = dps[2]
		}

		if strings.HasPrefix(alias, "$") {
			alias = alias[1:]
		}

		if fnumber, enumber := toolkit.IsStringNumber(fieldid, "."); enumber == nil {
			q = q.Aggr(op, fnumber, alias)
		} else {
			q = q.Aggr(op, fieldid, alias)
		}
	}

	c, e := q.Cursor(nil)
	if e != nil {
		return nil, errors.New("SummarizedLedgerSum: Preparing cursor error " + e.Error())
	}
	defer c.Close()

	ms := []toolkit.M{}
	e = c.Fetch(&ms, 0, false)
	if e != nil {
		return nil, errors.New("SummarizedLedgerSum: Fetch cursor error " + e.Error())
	}

	if c.Count() > 0 {
		e = c.Fetch(&ms, 0, false)
		if e != nil {
			return nil, errors.New("SummarizedLedgerSum: Fetch cursor error " + e.Error())
		}
	}

	if fnTransform != nil {
		for idx, m := range ms {
			e = fnTransform(&m)
			if e != nil {
				return nil, errors.New(toolkit.Sprintf("SummarizedLedgerSum: Transform error on index %d, %s",
					idx, e.Error()))
			}
		}
	}

	return ms, nil
}

func (s *LedgerSummary) Save() error {
	e := Save(s)
	if e != nil {
		return errors.New(toolkit.Sprintf("[%v-%v] Error found : ", s.TableName(), "save", e.Error()))
	}
	return e
}

type PivotParam struct {
	Dimensions []*PivotParamDimensions `json:"dimensions"`
	DataPoints []*PivotParamDataPoint  `json:"datapoints"`
}

type PivotParamDimensions struct {
	Field string `json:"field"`
	Type  string `json:"type"`
	Alias string `json:"alias"`
}

type PivotParamDataPoint struct {
	OP    string `json:"op"`
	Field string `json:"field"`
	Alias string `json:"alias"`
}

func (p *PivotParam) ParseDimensions() (res []string) {
	res = []string{}
	for _, each := range p.Dimensions {
		res = append(res, each.Field)
	}
	return
}

func (p *PivotParam) ParseDataPoints() (res []string) {
	for _, each := range p.DataPoints {
		parts := []string{each.OP, each.Field, each.Alias}

		if !strings.HasPrefix(parts[1], "$") {
			parts[1] = fmt.Sprintf("$%s", parts[1])
		}

		res = append(res, strings.Join(parts, ":"))
	}
	return
}

func (p *PivotParam) MapSummarizedLedger(data []toolkit.M) []toolkit.M {
	res := []toolkit.M{}
	metadata := map[string]string{}

	for i, each := range data {
		row := toolkit.M{}

		if i == 0 {
			// cache the metadata, only on first loop
			for key, val := range each {
				if key == "_id" {
					for key2 := range val.(toolkit.M) {
						keyv := key2

						for _, dimension := range p.Dimensions {
							if strings.ToLower(dimension.Field) == strings.ToLower(keyv) {
								keyv = strings.Replace(strings.Replace(dimension.Field, ".", "", -1), "_id", "_ID", -1)
							}
						}

						if key2 == "_id" {
							keyv = toolkit.TrimByString(keyv, "_")
						}

						metadata[fmt.Sprintf("%s.%s", key, key2)] = keyv
					}
				} else {
					keyv := key
					for _, each := range p.DataPoints {
						if strings.ToLower(each.Alias) == strings.ToLower(key) {
							keyv = strings.Replace(each.Alias, " ", "_", -1)
						}
					}
					metadata[key] = keyv
				}
			}
		}

		// flatten the data
		for key, val := range each {
			if key == "_id" {
				for key2, val2 := range val.(toolkit.M) {
					keyv := metadata[fmt.Sprintf("%s.%s", key, key2)]
					row.Set(keyv, val2)
				}
			} else {
				keyv := metadata[key]
				row.Set(keyv, val)
			}
		}

		res = append(res, row)
	}

	return res
}

func (p *PivotParam) GetPivotConfig(data []toolkit.M) toolkit.M {
	res := struct {
		SchemaModelFields   toolkit.M
		SchemaCubeDimension toolkit.M
		SchemaCubeMeasures  toolkit.M
		Columns             []toolkit.M
		Rows                []toolkit.M
		Measures            []string
	}{
		toolkit.M{},
		toolkit.M{},
		toolkit.M{},
		[]toolkit.M{},
		[]toolkit.M{},
		[]string{},
	}

	if len(data) > 0 {
		for key := range data[0] {
			for _, c := range p.Dimensions {
				a := strings.ToLower(strings.Replace(c.Field, ".", "", -1)) == strings.ToLower(key)
				b := strings.ToLower(toolkit.TrimByString(c.Field, "_")) == strings.ToLower(key)

				if a || b {
					if c.Type == "column" {
						res.Columns = append(res.Columns, toolkit.M{"name": key, "expand": false})
					} else {
						res.Rows = append(res.Rows, toolkit.M{"name": key, "expand": false})
					}

					caption := fmt.Sprintf("All %s", c.Alias)
					res.SchemaModelFields.Set(key, toolkit.M{"type": "string"})
					res.SchemaCubeDimension.Set(key, toolkit.M{"caption": caption})
				}
			}

			for _, c := range p.DataPoints {
				if strings.ToLower(strings.Replace(c.Alias, " ", "_", -1)) == strings.ToLower(key) {
					op := c.OP
					if op == "avg" {
						op = "average"
					}

					res.SchemaModelFields.Set(key, toolkit.M{"type": "number"})
					res.SchemaCubeMeasures.Set(key, toolkit.M{"field": key, "aggregate": op})
					res.Measures = append(res.Measures, key)
				}
			}
		}
	}

	resM, err := toolkit.ToM(res)
	if err != nil {
		fmt.Println(err.Error())
	}

	return resM
}
<|endoftext|>


<commit_before>package model
<commit_msg>product<commit_after>package model

import "log"

func Products() []Product {
	var providers []Product
	_, err = dbmap.Select(providers, "SELECT * FROM providers")
	log.Println(providers)
	return 0
}
<|endoftext|>


<commit_before>// 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 redfish

import (
	"encoding/json"

	"github.com/stmcginnis/gofish/school/common"
)

// ChassisType is a physical form of the chassis
type ChassisType string

const (
	// BladeChassisType is an enclosed or semi-enclosed, typically vertically-oriented, system
	//mchassis which must be plugged into a multi-system chassis to function normally.
	BladeChassisType ChassisType = "Blade"
	// CardChassisType is a loose device or circuit board intended to be installed in a system
	// or other enclosure.
	CardChassisType ChassisType = "Card"
	// CartridgeChassisType is a small self-contained system intended to be plugged into a multi-system
	// chassis.
	CartridgeChassisType ChassisType = "Cartridge"
	// ComponentChassisType is a small chassis, card, or device which contains devices for a particular
	// subsystem or function.
	ComponentChassisType ChassisType = "Component"
	// DrawerChassisType is an enclosed or semi-enclosed, typically horizontally-oriented, system
	// chassis which may be slid into a multi-system chassis.
	DrawerChassisType ChassisType = "Drawer"
	// EnclosureChassisType is a generic term for a chassis that does not fit any other description.
	EnclosureChassisType ChassisType = "Enclosure"
	// ExpansionChassisType is a chassis which expands the capabilities or capacity of another
	// chassis.
	ExpansionChassisType ChassisType = "Expansion"
	// IPBasedDriveChassisType is a chassis in a drive form factor with IP-based network connections.
	IPBasedDriveChassisType ChassisType = "IPBasedDrive"
	// ModuleChassisType is a small, typically removable, chassis or card which contains devices
	// for a particular subsystem or function.
	ModuleChassisType ChassisType = "Module"
	// OtherChassisType is a chassis that does not fit any of these definitions.
	OtherChassisType ChassisType = "Other"
	// PodChassisType is a collection of equipment racks in a large, likely transportable, container.
	PodChassisType ChassisType = "Pod"
	// RackChassisType is an equipment rack, typically a 19-inch wide freestanding unit.
	RackChassisType ChassisType = "Rack"
	// RackGroupChassisType is a group of racks which form a single entity or share infrastructure.
	RackGroupChassisType ChassisType = "RackGroup"
	// RackMountChassisType is a single system chassis designed specifically for mounting in an
	// equipment rack.
	RackMountChassisType ChassisType = "RackMount"
	// RowChassisType is a collection of equipment racks.
	RowChassisType ChassisType = "Row"
	// ShelfChassisType is an enclosed or semi-enclosed, typically horizontally-oriented, system
	// chassis which must be plugged into a multi-system chassis to function normally.
	ShelfChassisType ChassisType = "Shelf"
	// SidecarChassisType is a chassis that mates mechanically with another chassis to expand
	// its capabilities or capacity.
	SidecarChassisType ChassisType = "Sidecar"
	// SledChassisType is an enclosed or semi-enclosed, system chassis which must be plugged into
	// a multi-system chassis to function normally similar to a blade type chassis.
	SledChassisType ChassisType = "Sled"
	// StandAloneChassisType is a single, free-standing system, commonly called a tower or desktop
	// chassis.
	StandAloneChassisType ChassisType = "StandAlone"
	// StorageEnclosureChassisType is a chassis which encloses storage.
	StorageEnclosureChassisType ChassisType = "StorageEnclosure"
	// ZoneChassisType is a logical division or portion of a physical chassis that contains multiple
	// devices or systems that cannot be physically separated.
	ZoneChassisType ChassisType = "Zone"
)

// Chassis represents the physical components of a system.  This
// resource represents the sheet-metal confined spaces and logical zones such
// as racks, enclosures, chassis and all other containers. Subsystems (like sensors)
// that operate outside of a system's data plane (meaning the resources are not
// accessible to software running on the system) are linked either directly or
// indirectly through this resource.
type Chassis struct {
	common.Entity
	ChassisType     ChassisType   `json:"ChassisType"`
	Manufacturer    string        `json:"Manufacturer"`
	Model           string        `json:"Model"`
	SKU             string        `json:"SKU"`
	SerialNumber    string        `json:"SerialNumber"`
	Version         string        `json:"Version"`
	PartNumber      string        `json:"PartNumber"`
	AssetTag        string        `json:"AssetTag"`
	Status          common.Status `json:"Status"`
	thermal         string
	power           string
	computerSystems []string
	resourceBlocks  []string
	managedBy       []string
}

// UnmarshalJSON unmarshals a Chassis object from the raw JSON.
func (c *Chassis) UnmarshalJSON(b []byte) error {
	type temp Chassis
	type linkReference struct {
		ComputerSystems common.Links
		ResourceBlocks  common.Links
		ManagedBy       common.Links
	}
	var t struct {
		temp
		Thermal common.Link
		Power   common.Link
		Links   linkReference
	}

	err := json.Unmarshal(b, &t)
	if err != nil {
		return err
	}

	*c = Chassis(t.temp)

	// Extract the links to other entities for later
	c.thermal = string(t.Thermal)
	c.power = string(t.Power)
	c.computerSystems = t.Links.ComputerSystems.ToStrings()
	c.resourceBlocks = t.Links.ResourceBlocks.ToStrings()
	c.managedBy = t.Links.ManagedBy.ToStrings()

	return nil
}

// GetChassis will get a Chassis instance from the Redfish service.
func GetChassis(c common.Client, uri string) (*Chassis, error) {
	resp, err := c.Get(uri)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var chassis Chassis
	err = json.NewDecoder(resp.Body).Decode(&chassis)
	if err != nil {
		return nil, err
	}

	chassis.SetClient(c)
	return &chassis, nil
}

// ListReferencedChassis gets the collection of Chassis from a provided reference.
func ListReferencedChassis(c common.Client, link string) ([]*Chassis, error) {
	var result []*Chassis
	links, err := common.GetCollection(c, link)
	if err != nil {
		return result, err
	}

	for _, chassisLink := range links.ItemLinks {
		chassis, err := GetChassis(c, chassisLink)
		if err != nil {
			return result, err
		}
		result = append(result, chassis)
	}

	return result, nil
}

// ThermalInfo is reference to the thermal properties (fans, cooling, sensors)
// of this chassis.
type ThermalInfo struct {
	common.Entity
	Temperatures []struct {
		MemberID                  string `json:"MemberId"`
		Name                      string
		SensorNumber              int
		Status                    common.Status
		ReadingCelsius            int
		UpperThresholdNonCritical int
		UpperThresholdCritical    int
		UpperThresholdFatal       int
		LowerThresholdNonCritical int
		LowerThresholdCritical    int
		LowerThresholdFatal       int
		MinimumValue              int
		MaximumValue              int
		PhysicalContext           string
		RelatedItem               []common.Link
	}
	Fans []struct {
		MemberID                  string `json:"MemberId"`
		FanName                   string
		PhysicalContext           string
		Status                    common.Status
		ReadingRPM                int
		UpperThresholdNonCritical int
		UpperThresholdCritical    int
		UpperThresholdFatal       int
		LowerThresholdNonCritical int
		LowerThresholdCritical    int
		LowerThresholdFatal       int
		MinReadingRange           int
		MaxReadingRange           int
		Redundancy                []common.Link
		RelatedItem               []common.Link
	}
	Redundancy []struct {
		MemberID        string `json:"MemberId"`
		Name            string
		RedundancySet   []common.Link
		Mode            string
		Status          common.Status
		MinNumNeeded    int
		MaxNumSupported int
	}
}

// Thermal gets the thermal temperature and cooling information for the chassis
func (c *Chassis) Thermal() (*ThermalInfo, error) {
	if c.thermal == "" {
		return nil, nil
	}

	resp, err := c.Client.Get(c.thermal)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var thermal ThermalInfo
	err = json.NewDecoder(resp.Body).Decode(&thermal)
	if err != nil {
		return nil, err
	}

	return &thermal, nil
}

// PowerInfo provides the power properties (power supplies, power
// policies, sensors) of the chassis.
type PowerInfo struct {
	common.Entity
	PowerControl []struct {
		MemberID            string `json:"MemberId"`
		Name                string
		PowerConsumedWatts  int
		PowerRequestedWatts int
		PowerAvailableWatts int
		PowerCapacityWatts  int
		PowerAllocatedWatts int
		PowerMetrics        struct {
			IntervalInMin        int
			MinConsumedWatts     int
			MaxConsumedWatts     int
			AverageConsumedWatts int
		}
		PowerLimit struct {
			LimitInWatts   int
			LimitException string
			CorrectionInMS int `json:"CorrectionInMs"`
		}
		RelatedItem []common.Link
		Status      common.Status
	}
	Voltages []struct {
		MemberID                  string `json:"MemberId"`
		Name                      string
		SensorNumber              int
		Status                    common.Status
		ReadingVolts              int
		UpperThresholdNonCritical float32
		UpperThresholdCritical    float32
		UpperThresholdFatal       float32
		LowerThresholdNonCritical float32
		LowerThresholdCritical    float32
		LowerThresholdFatal       float32
		MinReadingRange           int
		MaxReadingRange           int
		PhysicalContext           string
		RelatedItem               []common.Link
	}
	PowerSupplies []struct {
		MemberID             string `json:"MemberId"`
		Name                 string
		Status               common.Status
		PowerSupplyType      string
		LineInputVoltageType string
		LineInputVoltage     int
		PowerCapacityWatts   int
		LastPowerOutputWatts int
		Model                string
		FirmwareVersion      string
		SerialNumber         string
		PartNumber           string
		SparePartNumber      string
		RelatedItem          []common.Link
	}
}

// Power gets the power information for the chassis
func (c *Chassis) Power() (*PowerInfo, error) {
	if c.power == "" {
		return nil, nil
	}

	resp, err := c.Client.Get(c.power)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var power PowerInfo
	err = json.NewDecoder(resp.Body).Decode(&power)
	if err != nil {
		return nil, err
	}

	return &power, nil
}

// ComputerSystems returns the collection of systems from this chassis
func (c *Chassis) ComputerSystems() ([]*ComputerSystem, error) {
	var result []*ComputerSystem
	for _, uri := range c.computerSystems {
		cs, err := GetComputerSystem(c.Client, uri)
		if err != nil {
			return nil, err
		}

		result = append(result, cs)
	}

	return result, nil
}

// ManagedBy gets the collection of managers of this chassis
func (c *Chassis) ManagedBy() ([]*Manager, error) {
	var result []*Manager
	for _, uri := range c.managedBy {
		manager, err := GetManager(c.Client, uri)
		if err != nil {
			return nil, err
		}

		result = append(result, manager)
	}

	return result, nil
}
<commit_msg>change data type to float32<commit_after>// 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 redfish

import (
	"encoding/json"

	"github.com/stmcginnis/gofish/school/common"
)

// ChassisType is a physical form of the chassis
type ChassisType string

const (
	// BladeChassisType is an enclosed or semi-enclosed, typically vertically-oriented, system
	//mchassis which must be plugged into a multi-system chassis to function normally.
	BladeChassisType ChassisType = "Blade"
	// CardChassisType is a loose device or circuit board intended to be installed in a system
	// or other enclosure.
	CardChassisType ChassisType = "Card"
	// CartridgeChassisType is a small self-contained system intended to be plugged into a multi-system
	// chassis.
	CartridgeChassisType ChassisType = "Cartridge"
	// ComponentChassisType is a small chassis, card, or device which contains devices for a particular
	// subsystem or function.
	ComponentChassisType ChassisType = "Component"
	// DrawerChassisType is an enclosed or semi-enclosed, typically horizontally-oriented, system
	// chassis which may be slid into a multi-system chassis.
	DrawerChassisType ChassisType = "Drawer"
	// EnclosureChassisType is a generic term for a chassis that does not fit any other description.
	EnclosureChassisType ChassisType = "Enclosure"
	// ExpansionChassisType is a chassis which expands the capabilities or capacity of another
	// chassis.
	ExpansionChassisType ChassisType = "Expansion"
	// IPBasedDriveChassisType is a chassis in a drive form factor with IP-based network connections.
	IPBasedDriveChassisType ChassisType = "IPBasedDrive"
	// ModuleChassisType is a small, typically removable, chassis or card which contains devices
	// for a particular subsystem or function.
	ModuleChassisType ChassisType = "Module"
	// OtherChassisType is a chassis that does not fit any of these definitions.
	OtherChassisType ChassisType = "Other"
	// PodChassisType is a collection of equipment racks in a large, likely transportable, container.
	PodChassisType ChassisType = "Pod"
	// RackChassisType is an equipment rack, typically a 19-inch wide freestanding unit.
	RackChassisType ChassisType = "Rack"
	// RackGroupChassisType is a group of racks which form a single entity or share infrastructure.
	RackGroupChassisType ChassisType = "RackGroup"
	// RackMountChassisType is a single system chassis designed specifically for mounting in an
	// equipment rack.
	RackMountChassisType ChassisType = "RackMount"
	// RowChassisType is a collection of equipment racks.
	RowChassisType ChassisType = "Row"
	// ShelfChassisType is an enclosed or semi-enclosed, typically horizontally-oriented, system
	// chassis which must be plugged into a multi-system chassis to function normally.
	ShelfChassisType ChassisType = "Shelf"
	// SidecarChassisType is a chassis that mates mechanically with another chassis to expand
	// its capabilities or capacity.
	SidecarChassisType ChassisType = "Sidecar"
	// SledChassisType is an enclosed or semi-enclosed, system chassis which must be plugged into
	// a multi-system chassis to function normally similar to a blade type chassis.
	SledChassisType ChassisType = "Sled"
	// StandAloneChassisType is a single, free-standing system, commonly called a tower or desktop
	// chassis.
	StandAloneChassisType ChassisType = "StandAlone"
	// StorageEnclosureChassisType is a chassis which encloses storage.
	StorageEnclosureChassisType ChassisType = "StorageEnclosure"
	// ZoneChassisType is a logical division or portion of a physical chassis that contains multiple
	// devices or systems that cannot be physically separated.
	ZoneChassisType ChassisType = "Zone"
)

// Chassis represents the physical components of a system.  This
// resource represents the sheet-metal confined spaces and logical zones such
// as racks, enclosures, chassis and all other containers. Subsystems (like sensors)
// that operate outside of a system's data plane (meaning the resources are not
// accessible to software running on the system) are linked either directly or
// indirectly through this resource.
type Chassis struct {
	common.Entity
	ChassisType     ChassisType   `json:"ChassisType"`
	Manufacturer    string        `json:"Manufacturer"`
	Model           string        `json:"Model"`
	SKU             string        `json:"SKU"`
	SerialNumber    string        `json:"SerialNumber"`
	Version         string        `json:"Version"`
	PartNumber      string        `json:"PartNumber"`
	AssetTag        string        `json:"AssetTag"`
	Status          common.Status `json:"Status"`
	thermal         string
	power           string
	computerSystems []string
	resourceBlocks  []string
	managedBy       []string
}

// UnmarshalJSON unmarshals a Chassis object from the raw JSON.
func (c *Chassis) UnmarshalJSON(b []byte) error {
	type temp Chassis
	type linkReference struct {
		ComputerSystems common.Links
		ResourceBlocks  common.Links
		ManagedBy       common.Links
	}
	var t struct {
		temp
		Thermal common.Link
		Power   common.Link
		Links   linkReference
	}

	err := json.Unmarshal(b, &t)
	if err != nil {
		return err
	}

	*c = Chassis(t.temp)

	// Extract the links to other entities for later
	c.thermal = string(t.Thermal)
	c.power = string(t.Power)
	c.computerSystems = t.Links.ComputerSystems.ToStrings()
	c.resourceBlocks = t.Links.ResourceBlocks.ToStrings()
	c.managedBy = t.Links.ManagedBy.ToStrings()

	return nil
}

// GetChassis will get a Chassis instance from the Redfish service.
func GetChassis(c common.Client, uri string) (*Chassis, error) {
	resp, err := c.Get(uri)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var chassis Chassis
	err = json.NewDecoder(resp.Body).Decode(&chassis)
	if err != nil {
		return nil, err
	}

	chassis.SetClient(c)
	return &chassis, nil
}

// ListReferencedChassis gets the collection of Chassis from a provided reference.
func ListReferencedChassis(c common.Client, link string) ([]*Chassis, error) {
	var result []*Chassis
	links, err := common.GetCollection(c, link)
	if err != nil {
		return result, err
	}

	for _, chassisLink := range links.ItemLinks {
		chassis, err := GetChassis(c, chassisLink)
		if err != nil {
			return result, err
		}
		result = append(result, chassis)
	}

	return result, nil
}

// ThermalInfo is reference to the thermal properties (fans, cooling, sensors)
// of this chassis.
type ThermalInfo struct {
	common.Entity
	Temperatures []struct {
		MemberID                  string `json:"MemberId"`
		Name                      string
		SensorNumber              int
		Status                    common.Status
		ReadingCelsius            float32
		UpperThresholdNonCritical float32
		UpperThresholdCritical    float32
		UpperThresholdFatal       float32
		LowerThresholdNonCritical float32
		LowerThresholdCritical    float32
		LowerThresholdFatal       float32
		MinimumValue              float32
		MaximumValue              float32
		PhysicalContext           string
		RelatedItem               []common.Link
	}
	Fans []struct {
		MemberID                  string `json:"MemberId"`
		FanName                   string
		PhysicalContext           string
		Status                    common.Status
		ReadingRPM                float32
		UpperThresholdNonCritical float32
		UpperThresholdCritical    float32
		UpperThresholdFatal       float32
		LowerThresholdNonCritical float32
		LowerThresholdCritical    float32
		LowerThresholdFatal       float32
		MinReadingRange           float32
		MaxReadingRange           float32
		Redundancy                []common.Link
		RelatedItem               []common.Link
	}
	Redundancy []struct {
		MemberID        string `json:"MemberId"`
		Name            string
		RedundancySet   []common.Link
		Mode            string
		Status          common.Status
		MinNumNeeded    int
		MaxNumSupported int
	}
}

// Thermal gets the thermal temperature and cooling information for the chassis
func (c *Chassis) Thermal() (*ThermalInfo, error) {
	if c.thermal == "" {
		return nil, nil
	}

	resp, err := c.Client.Get(c.thermal)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var thermal ThermalInfo
	err = json.NewDecoder(resp.Body).Decode(&thermal)
	if err != nil {
		return nil, err
	}

	return &thermal, nil
}

// PowerInfo provides the power properties (power supplies, power
// policies, sensors) of the chassis.
type PowerInfo struct {
	common.Entity
	PowerControl []struct {
		MemberID            string `json:"MemberId"`
		Name                string
		PowerConsumedWatts  float32
		PowerRequestedWatts float32
		PowerAvailableWatts float32
		PowerCapacityWatts  float32
		PowerAllocatedWatts float32
		PowerMetrics        struct {
			IntervalInMin        int
			MinConsumedWatts     float32
			MaxConsumedWatts     float32
			AverageConsumedWatts float32
		}
		PowerLimit struct {
			LimitInWatts   float32
			LimitException string
			CorrectionInMS float32 `json:"CorrectionInMs"`
		}
		RelatedItem []common.Link
		Status      common.Status
	}
	Voltages []struct {
		MemberID                  string `json:"MemberId"`
		Name                      string
		SensorNumber              int
		Status                    common.Status
		ReadingVolts              float32
		UpperThresholdNonCritical float32
		UpperThresholdCritical    float32
		UpperThresholdFatal       float32
		LowerThresholdNonCritical float32
		LowerThresholdCritical    float32
		LowerThresholdFatal       float32
		MinReadingRange           float32
		MaxReadingRange           float32
		PhysicalContext           string
		RelatedItem               []common.Link
	}
	PowerSupplies []struct {
		MemberID             string `json:"MemberId"`
		Name                 string
		Status               common.Status
		PowerSupplyType      string
		LineInputVoltageType string
		LineInputVoltage     float32
		PowerCapacityWatts   float32
		LastPowerOutputWatts float32
		Model                string
		FirmwareVersion      string
		SerialNumber         string
		PartNumber           string
		SparePartNumber      string
		RelatedItem          []common.Link
	}
}

// Power gets the power information for the chassis
func (c *Chassis) Power() (*PowerInfo, error) {
	if c.power == "" {
		return nil, nil
	}

	resp, err := c.Client.Get(c.power)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var power PowerInfo
	err = json.NewDecoder(resp.Body).Decode(&power)
	if err != nil {
		return nil, err
	}

	return &power, nil
}

// ComputerSystems returns the collection of systems from this chassis
func (c *Chassis) ComputerSystems() ([]*ComputerSystem, error) {
	var result []*ComputerSystem
	for _, uri := range c.computerSystems {
		cs, err := GetComputerSystem(c.Client, uri)
		if err != nil {
			return nil, err
		}

		result = append(result, cs)
	}

	return result, nil
}

// ManagedBy gets the collection of managers of this chassis
func (c *Chassis) ManagedBy() ([]*Manager, error) {
	var result []*Manager
	for _, uri := range c.managedBy {
		manager, err := GetManager(c.Client, uri)
		if err != nil {
			return nil, err
		}

		result = append(result, manager)
	}

	return result, nil
}
<|endoftext|>