<commit_before>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package types

import (
	"fmt"
	"strings"

	"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud"
	"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/filter"
	"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
	"google.golang.org/api/compute/v1"
	"k8s.io/legacy-cloud-providers/gce"
)

// NewAdapter takes a Cloud and returns a NetworkEndpointGroupCloud.
func NewAdapter(g *gce.Cloud) NetworkEndpointGroupCloud {
	return &cloudProviderAdapter{
		c:             g.Compute(),
		networkURL:    g.NetworkURL(),
		subnetworkURL: g.SubnetworkURL(),
	}
}

// cloudProviderAdapter is a temporary shim to consolidate accesses to
// Cloud and push them outside of this package.
type cloudProviderAdapter struct {
	c             cloud.Cloud
	networkURL    string
	subnetworkURL string
}

// GetNetworkEndpointGroup inmplements NetworkEndpointGroupCloud.
func (a *cloudProviderAdapter) GetNetworkEndpointGroup(name string, zone string) (*compute.NetworkEndpointGroup, error) {
	ctx, cancel := cloud.ContextWithCallTimeout()
	defer cancel()

	return a.c.NetworkEndpointGroups().Get(ctx, meta.ZonalKey(name, zone))
}

// ListNetworkEndpointGroup implements NetworkEndpointGroupCloud.
func (a *cloudProviderAdapter) ListNetworkEndpointGroup(zone string) ([]*compute.NetworkEndpointGroup, error) {
	ctx, cancel := cloud.ContextWithCallTimeout()
	defer cancel()

	return a.c.NetworkEndpointGroups().List(ctx, zone, filter.None)
}

// AggregatedListNetworkEndpointGroup returns a map of zone -> endpoint group.
func (a *cloudProviderAdapter) AggregatedListNetworkEndpointGroup() (map[string][]*compute.NetworkEndpointGroup, error) {
	ctx, cancel := cloud.ContextWithCallTimeout()
	defer cancel()

	// TODO: filter for the region the cluster is in.
	all, err := a.c.NetworkEndpointGroups().AggregatedList(ctx, filter.None)
	if err != nil {
		return nil, err
	}
	ret := map[string][]*compute.NetworkEndpointGroup{}
	for key, byZone := range all {
		// key is "zones/<zone name>"
		parts := strings.Split(key, "/")
		if len(parts) != 2 {
			return nil, fmt.Errorf("invalid key for AggregatedListNetworkEndpointGroup: %q", key)
		}
		zone := parts[1]
		ret[zone] = append(ret[zone], byZone...)
	}
	return ret, nil
}

// CreateNetworkEndpointGroup implements NetworkEndpointGroupCloud.
func (a *cloudProviderAdapter) CreateNetworkEndpointGroup(neg *compute.NetworkEndpointGroup, zone string) error {
	ctx, cancel := cloud.ContextWithCallTimeout()
	defer cancel()

	return a.c.NetworkEndpointGroups().Insert(ctx, meta.ZonalKey(neg.Name, zone), neg)
}

// DeleteNetworkEndpointGroup implements NetworkEndpointGroupCloud.
func (a *cloudProviderAdapter) DeleteNetworkEndpointGroup(name string, zone string) error {
	ctx, cancel := cloud.ContextWithCallTimeout()
	defer cancel()

	return a.c.NetworkEndpointGroups().Delete(ctx, meta.ZonalKey(name, zone))
}

// AttachNetworkEndpoints implements NetworkEndpointGroupCloud.
func (a cloudProviderAdapter) AttachNetworkEndpoints(name, zone string, endpoints []*compute.NetworkEndpoint) error {
	ctx, cancel := cloud.ContextWithCallTimeout()
	defer cancel()

	req := &compute.NetworkEndpointGroupsAttachEndpointsRequest{NetworkEndpoints: endpoints}
	return a.c.NetworkEndpointGroups().AttachNetworkEndpoints(ctx, meta.ZonalKey(name, zone), req)
}

// DetachNetworkEndpoints implements NetworkEndpointGroupCloud.
func (a *cloudProviderAdapter) DetachNetworkEndpoints(name, zone string, endpoints []*compute.NetworkEndpoint) error {
	ctx, cancel := cloud.ContextWithCallTimeout()
	defer cancel()

	req := &compute.NetworkEndpointGroupsDetachEndpointsRequest{NetworkEndpoints: endpoints}
	return a.c.NetworkEndpointGroups().DetachNetworkEndpoints(ctx, meta.ZonalKey(name, zone), req)
}

// ListNetworkEndpoints implements NetworkEndpointGroupCloud.
func (a *cloudProviderAdapter) ListNetworkEndpoints(name, zone string, showHealthStatus bool) ([]*compute.NetworkEndpointWithHealthStatus, error) {
	ctx, cancel := cloud.ContextWithCallTimeout()
	defer cancel()

	healthStatus := "SKIP"
	if showHealthStatus {
		healthStatus = "SHOW"
	}
	req := &compute.NetworkEndpointGroupsListEndpointsRequest{HealthStatus: healthStatus}
	return a.c.NetworkEndpointGroups().ListNetworkEndpoints(ctx, meta.ZonalKey(name, zone), req, filter.None)
}

// NetworkURL implements NetworkEndpointGroupCloud.
func (a *cloudProviderAdapter) NetworkURL() string {
	return a.networkURL
}

// SubnetworkURL implements NetworkEndpointGroupCloud.
func (a *cloudProviderAdapter) SubnetworkURL() string {
	return a.subnetworkURL
}
<commit_msg>fix a bug where AggregatedListNEG will throw error when non zonal NEG is included in the API response<commit_after>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package types

import (
	"strings"

	"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud"
	"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/filter"
	"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
	"google.golang.org/api/compute/v1"
	"k8s.io/klog"
	"k8s.io/legacy-cloud-providers/gce"
)

const (
	// aggregatedListZonalKeyPrefix is the prefix for the zonal key from AggregatedList
	aggregatedListZonalKeyPrefix = "zones"
	// aggregatedListGlobalKey is the global key from AggregatedList
	aggregatedListGlobalKey = "global"
)

// NewAdapter takes a Cloud and returns a NetworkEndpointGroupCloud.
func NewAdapter(g *gce.Cloud) NetworkEndpointGroupCloud {
	return &cloudProviderAdapter{
		c:             g.Compute(),
		networkURL:    g.NetworkURL(),
		subnetworkURL: g.SubnetworkURL(),
	}
}

// cloudProviderAdapter is a temporary shim to consolidate accesses to
// Cloud and push them outside of this package.
type cloudProviderAdapter struct {
	c             cloud.Cloud
	networkURL    string
	subnetworkURL string
}

// GetNetworkEndpointGroup inmplements NetworkEndpointGroupCloud.
func (a *cloudProviderAdapter) GetNetworkEndpointGroup(name string, zone string) (*compute.NetworkEndpointGroup, error) {
	ctx, cancel := cloud.ContextWithCallTimeout()
	defer cancel()

	return a.c.NetworkEndpointGroups().Get(ctx, meta.ZonalKey(name, zone))
}

// ListNetworkEndpointGroup implements NetworkEndpointGroupCloud.
func (a *cloudProviderAdapter) ListNetworkEndpointGroup(zone string) ([]*compute.NetworkEndpointGroup, error) {
	ctx, cancel := cloud.ContextWithCallTimeout()
	defer cancel()

	return a.c.NetworkEndpointGroups().List(ctx, zone, filter.None)
}

// AggregatedListNetworkEndpointGroup returns a map of zone -> endpoint group.
func (a *cloudProviderAdapter) AggregatedListNetworkEndpointGroup() (map[string][]*compute.NetworkEndpointGroup, error) {
	ctx, cancel := cloud.ContextWithCallTimeout()
	defer cancel()

	// TODO: filter for the region the cluster is in.
	all, err := a.c.NetworkEndpointGroups().AggregatedList(ctx, filter.None)
	if err != nil {
		return nil, err
	}
	ret := map[string][]*compute.NetworkEndpointGroup{}
	for key, byZone := range all {
		// key is scope
		// zonal key is "zones/<zone name>"
		// regional key is "regions/<region name>"
		// global key is "global"
		// TODO: use cloud provider meta.KeyType and scope name as key
		parts := strings.Split(key, "/")
		if len(parts) == 1 && parts[0] == aggregatedListGlobalKey {
			klog.V(4).Infof("Ignoring key %q as it is global", key)
			continue
		}
		if len(parts) != 2 || parts[0] != aggregatedListZonalKeyPrefix {
			klog.Warningf("Key %q is not in a known format, ignoring", key)
			continue
		}
		zone := parts[1]
		ret[zone] = append(ret[zone], byZone...)
	}
	return ret, nil
}

// CreateNetworkEndpointGroup implements NetworkEndpointGroupCloud.
func (a *cloudProviderAdapter) CreateNetworkEndpointGroup(neg *compute.NetworkEndpointGroup, zone string) error {
	ctx, cancel := cloud.ContextWithCallTimeout()
	defer cancel()

	return a.c.NetworkEndpointGroups().Insert(ctx, meta.ZonalKey(neg.Name, zone), neg)
}

// DeleteNetworkEndpointGroup implements NetworkEndpointGroupCloud.
func (a *cloudProviderAdapter) DeleteNetworkEndpointGroup(name string, zone string) error {
	ctx, cancel := cloud.ContextWithCallTimeout()
	defer cancel()

	return a.c.NetworkEndpointGroups().Delete(ctx, meta.ZonalKey(name, zone))
}

// AttachNetworkEndpoints implements NetworkEndpointGroupCloud.
func (a cloudProviderAdapter) AttachNetworkEndpoints(name, zone string, endpoints []*compute.NetworkEndpoint) error {
	ctx, cancel := cloud.ContextWithCallTimeout()
	defer cancel()

	req := &compute.NetworkEndpointGroupsAttachEndpointsRequest{NetworkEndpoints: endpoints}
	return a.c.NetworkEndpointGroups().AttachNetworkEndpoints(ctx, meta.ZonalKey(name, zone), req)
}

// DetachNetworkEndpoints implements NetworkEndpointGroupCloud.
func (a *cloudProviderAdapter) DetachNetworkEndpoints(name, zone string, endpoints []*compute.NetworkEndpoint) error {
	ctx, cancel := cloud.ContextWithCallTimeout()
	defer cancel()

	req := &compute.NetworkEndpointGroupsDetachEndpointsRequest{NetworkEndpoints: endpoints}
	return a.c.NetworkEndpointGroups().DetachNetworkEndpoints(ctx, meta.ZonalKey(name, zone), req)
}

// ListNetworkEndpoints implements NetworkEndpointGroupCloud.
func (a *cloudProviderAdapter) ListNetworkEndpoints(name, zone string, showHealthStatus bool) ([]*compute.NetworkEndpointWithHealthStatus, error) {
	ctx, cancel := cloud.ContextWithCallTimeout()
	defer cancel()

	healthStatus := "SKIP"
	if showHealthStatus {
		healthStatus = "SHOW"
	}
	req := &compute.NetworkEndpointGroupsListEndpointsRequest{HealthStatus: healthStatus}
	return a.c.NetworkEndpointGroups().ListNetworkEndpoints(ctx, meta.ZonalKey(name, zone), req, filter.None)
}

// NetworkURL implements NetworkEndpointGroupCloud.
func (a *cloudProviderAdapter) NetworkURL() string {
	return a.networkURL
}

// SubnetworkURL implements NetworkEndpointGroupCloud.
func (a *cloudProviderAdapter) SubnetworkURL() string {
	return a.subnetworkURL
}
<|endoftext|>


<commit_before>//
// Last.Backend LLC CONFIDENTIAL
// __________________
//
// [2014] - [2018] Last.Backend LLC
// All Rights Reserved.
//
// NOTICE:  All information contained herein is, and remains
// the property of Last.Backend LLC and its suppliers,
// if any.  The intellectual and technical concepts contained
// herein are proprietary to Last.Backend LLC
// and its suppliers and may be covered by Russian Federation and Foreign Patents,
// patents in process, and are protected by trade secret or copyright law.
// Dissemination of this information or reproduction of this material
// is strictly forbidden unless prior written permission is obtained
// from Last.Backend LLC.
//

package docker

import (
	"fmt"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/mount"
	"github.com/docker/docker/api/types/network"
	"github.com/docker/docker/api/types/strslice"
	"github.com/docker/go-connections/nat"
	"github.com/lastbackend/lastbackend/pkg/distribution/types"
	"strconv"
	"github.com/lastbackend/dynamic/pkg/log"
)

func GetConfig(spec *types.SpecTemplateContainer, secrets map[string]*types.Secret) *container.Config {

	var volumes map[string]struct{}

	ports := make(nat.PortSet, 0)
	volumes = make(map[string]struct{}, 0)

	for _, p := range spec.Ports {
		port := nat.Port(strconv.Itoa(int(p.ContainerPort)))
		ports[port] = struct{}{}
	}

	var envs []string
	for _, e := range spec.EnvVars {
		var env string
		if e.From.Name != types.EmptyString {
			if _, ok := secrets[e.From.Name]; !ok {
				continue
			}
			v, err := secrets[e.From.Name].DecodeSecretTextData(e.From.Key)
			if err != nil {
				log.Error(err.Error())
				continue
			}
			env = fmt.Sprintf("%s=%s", e.Name, v)
		} else {
			env = fmt.Sprintf("%s=%s", e.Name, e.Value)
		}
		envs = append(envs, env)
	}

	log.Info(envs)

	return &container.Config{
		Hostname:     spec.Network.Hostname,
		Domainname:   spec.Network.Domain,
		Env:          envs,
		ExposedPorts: ports,
		Volumes:      volumes,
		Labels:       spec.Labels,
		Cmd:          strslice.StrSlice(spec.Exec.Command),
		Entrypoint:   strslice.StrSlice(spec.Exec.Entrypoint),
		Image:        spec.Image.Name,
		WorkingDir:   spec.Exec.Workdir,
	}
}

func GetHostConfig(spec *types.SpecTemplateContainer) *container.HostConfig {

	rPolicy := container.RestartPolicy{
		Name:              spec.RestartPolicy.Policy,
		MaximumRetryCount: spec.RestartPolicy.Attempt,
	}

	resources := container.Resources{
		Memory:    spec.Resources.Request.RAM * 1024 * 1024,
		CPUShares: spec.Resources.Request.CPU,
	}

	var (
		ports  = make(nat.PortMap, 0)
		mounts []mount.Mount
		binds  []string
		links  []string
	)

	for _, v := range spec.Volumes {
		binds = append(binds, fmt.Sprintf("%s:%s:%s", v.Name, v.Path, v.Mode))
	}

	for _, l := range spec.Links {
		links = append(links, fmt.Sprintf("%s:%s", l.Link, l.Alias))
	}

	log := container.LogConfig{}
	ports = make(nat.PortMap)

	return &container.HostConfig{
		Binds:           binds,
		LogConfig:       log,
		NetworkMode:     container.NetworkMode(spec.Network.Mode),
		PortBindings:    ports,
		DNS:             spec.DNS.Server,
		DNSOptions:      spec.DNS.Options,
		DNSSearch:       spec.DNS.Search,
		RestartPolicy:   rPolicy,
		Resources:       resources,
		Mounts:          mounts,
		Links:           links,
		Privileged:      spec.Security.Privileged,
		ExtraHosts:      spec.ExtraHosts,
		PublishAllPorts: spec.PublishAllPorts,
		AutoRemove:      spec.AutoRemove,
	}
}

func GetNetworkConfig(spec *types.SpecTemplateContainer) *network.NetworkingConfig {

	cfg := &network.NetworkingConfig{
		//EndpointsConfig: make(map[string]*network.EndpointSettings),
	}

	//endpoint := &network.EndpointSettings{
	//	NetworkID: spec.Subnet.SubnetSpec,
	//}
	//cfg.EndpointsConfig["lo"] = endpoint

	return cfg
}
<commit_msg>update deps<commit_after>//
// Last.Backend LLC CONFIDENTIAL
// __________________
//
// [2014] - [2018] Last.Backend LLC
// All Rights Reserved.
//
// NOTICE:  All information contained herein is, and remains
// the property of Last.Backend LLC and its suppliers,
// if any.  The intellectual and technical concepts contained
// herein are proprietary to Last.Backend LLC
// and its suppliers and may be covered by Russian Federation and Foreign Patents,
// patents in process, and are protected by trade secret or copyright law.
// Dissemination of this information or reproduction of this material
// is strictly forbidden unless prior written permission is obtained
// from Last.Backend LLC.
//

package docker

import (
	"fmt"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/mount"
	"github.com/docker/docker/api/types/network"
	"github.com/docker/docker/api/types/strslice"
	"github.com/docker/go-connections/nat"
	"github.com/lastbackend/lastbackend/pkg/distribution/types"
	"strconv"
	"github.com/lastbackend/lastbackend/pkg/log"
)

func GetConfig(spec *types.SpecTemplateContainer, secrets map[string]*types.Secret) *container.Config {

	var volumes map[string]struct{}

	ports := make(nat.PortSet, 0)
	volumes = make(map[string]struct{}, 0)

	for _, p := range spec.Ports {
		port := nat.Port(strconv.Itoa(int(p.ContainerPort)))
		ports[port] = struct{}{}
	}

	var envs []string
	for _, e := range spec.EnvVars {
		var env string
		if e.From.Name != types.EmptyString {
			if _, ok := secrets[e.From.Name]; !ok {
				continue
			}
			v, err := secrets[e.From.Name].DecodeSecretTextData(e.From.Key)
			if err != nil {
				log.Error(err.Error())
				continue
			}
			env = fmt.Sprintf("%s=%s", e.Name, v)
		} else {
			env = fmt.Sprintf("%s=%s", e.Name, e.Value)
		}
		envs = append(envs, env)
	}

	log.Info(envs)

	return &container.Config{
		Hostname:     spec.Network.Hostname,
		Domainname:   spec.Network.Domain,
		Env:          envs,
		ExposedPorts: ports,
		Volumes:      volumes,
		Labels:       spec.Labels,
		Cmd:          strslice.StrSlice(spec.Exec.Command),
		Entrypoint:   strslice.StrSlice(spec.Exec.Entrypoint),
		Image:        spec.Image.Name,
		WorkingDir:   spec.Exec.Workdir,
	}
}

func GetHostConfig(spec *types.SpecTemplateContainer) *container.HostConfig {

	rPolicy := container.RestartPolicy{
		Name:              spec.RestartPolicy.Policy,
		MaximumRetryCount: spec.RestartPolicy.Attempt,
	}

	resources := container.Resources{
		Memory:    spec.Resources.Request.RAM * 1024 * 1024,
		CPUShares: spec.Resources.Request.CPU,
	}

	var (
		ports  = make(nat.PortMap, 0)
		mounts []mount.Mount
		binds  []string
		links  []string
	)

	for _, v := range spec.Volumes {
		binds = append(binds, fmt.Sprintf("%s:%s:%s", v.Name, v.Path, v.Mode))
	}

	for _, l := range spec.Links {
		links = append(links, fmt.Sprintf("%s:%s", l.Link, l.Alias))
	}

	log := container.LogConfig{}
	ports = make(nat.PortMap)

	return &container.HostConfig{
		Binds:           binds,
		LogConfig:       log,
		NetworkMode:     container.NetworkMode(spec.Network.Mode),
		PortBindings:    ports,
		DNS:             spec.DNS.Server,
		DNSOptions:      spec.DNS.Options,
		DNSSearch:       spec.DNS.Search,
		RestartPolicy:   rPolicy,
		Resources:       resources,
		Mounts:          mounts,
		Links:           links,
		Privileged:      spec.Security.Privileged,
		ExtraHosts:      spec.ExtraHosts,
		PublishAllPorts: spec.PublishAllPorts,
		AutoRemove:      spec.AutoRemove,
	}
}

func GetNetworkConfig(spec *types.SpecTemplateContainer) *network.NetworkingConfig {

	cfg := &network.NetworkingConfig{
		//EndpointsConfig: make(map[string]*network.EndpointSettings),
	}

	//endpoint := &network.EndpointSettings{
	//	NetworkID: spec.Subnet.SubnetSpec,
	//}
	//cfg.EndpointsConfig["lo"] = endpoint

	return cfg
}
<|endoftext|>


<commit_before>// Copyright 2018 The OpenPitrix Authors. All rights reserved.
// Use of this source code is governed by a Apache license
// that can be found in the LICENSE file.

package account

import (
	"context"

	pbam "openpitrix.io/iam/pkg/pb"
	"openpitrix.io/openpitrix/pkg/client/iam/am"
	"openpitrix.io/openpitrix/pkg/gerr"
	"openpitrix.io/openpitrix/pkg/logger"
	"openpitrix.io/openpitrix/pkg/models"
	"openpitrix.io/openpitrix/pkg/pb"
	"openpitrix.io/openpitrix/pkg/util/ctxutil"
	"openpitrix.io/openpitrix/pkg/util/stringutil"
)

var (
	_           pb.AccessManagerServer = (*Server)(nil)
	amClient, _                        = am.NewClient()
)

func validateRoleUser(ctx context.Context, roleId, userId string) bool {
	response, err := amClient.GetRoleWithUser(ctx, &pbam.GetRoleRequest{
		RoleId: roleId,
	})
	if err != nil {
		logger.Error(ctx, "Get role with user failed: %+v", err)
		return false
	}
	if stringutil.StringIn(userId, response.Role.UserIdSet) {
		return true
	} else {
		return false
	}
}

func (p *Server) CanDo(ctx context.Context, req *pb.CanDoRequest) (*pb.CanDoResponse, error) {
	v, err := amClient.CanDo(ctx, &pbam.CanDoRequest{
		UserId:    req.UserId,
		Url:       req.Url,
		UrlMethod: req.UrlMethod,
		ApiMethod: req.ApiMethod,
	})
	if err != nil {
		return nil, err
	}

	return &pb.CanDoResponse{
		UserId:     v.UserId,
		AccessPath: v.AccessPath,
		OwnerPath:  v.OwnerPath,
	}, nil
}

func (p *Server) GetRoleModule(ctx context.Context, req *pb.GetRoleModuleRequest) (*pb.GetRoleModuleResponse, error) {
	response, err := amClient.GetRoleModule(ctx, &pbam.GetRoleModuleRequest{
		RoleId: req.RoleId,
	})
	if err != nil {
		return nil, err
	}

	return &pb.GetRoleModuleResponse{
		RoleId: req.RoleId,
		Module: models.ToPbModule(response.Module),
	}, nil
}

func (p *Server) ModifyRoleModule(ctx context.Context, req *pb.ModifyRoleModuleRequest) (*pb.ModifyRoleModuleResponse, error) {
	_, err := amClient.ModifyRoleModule(ctx, &pbam.ModifyRoleModuleRequest{
		RoleId: req.RoleId,
		Module: models.ToAmModule(req.Module),
	})
	if err != nil {
		return nil, err
	}

	return &pb.ModifyRoleModuleResponse{
		RoleId: req.RoleId,
	}, nil
}

func (p *Server) CreateRole(ctx context.Context, req *pb.CreateRoleRequest) (*pb.CreateRoleResponse, error) {
	s := ctxutil.GetSender(ctx)
	response, err := amClient.CreateRole(ctx, &pbam.CreateRoleRequest{
		RoleName:    req.RoleName,
		Description: req.Description,
		Portal:      req.Portal,
		Owner:       s.GetOwnerPath().Owner(),
		OwnerPath:   string(s.GetOwnerPath()),
	})
	if err != nil {
		return nil, err
	}

	return &pb.CreateRoleResponse{
		RoleId: response.RoleId,
	}, nil
}

func (p *Server) DeleteRoles(ctx context.Context, req *pb.DeleteRolesRequest) (*pb.DeleteRolesResponse, error) {
	_, err := amClient.DeleteRoles(ctx, &pbam.DeleteRolesRequest{
		RoleId: req.RoleId,
	})
	if err != nil {
		return nil, err
	}

	return &pb.DeleteRolesResponse{
		RoleId: req.RoleId,
	}, nil
}

func (p *Server) ModifyRole(ctx context.Context, req *pb.ModifyRoleRequest) (*pb.ModifyRoleResponse, error) {
	_, err := amClient.ModifyRole(ctx, &pbam.ModifyRoleRequest{
		RoleId:      req.RoleId,
		RoleName:    req.RoleName,
		Description: req.Description,
	})
	if err != nil {
		return nil, err
	}

	return &pb.ModifyRoleResponse{
		RoleId: req.RoleId,
	}, nil
}

func (p *Server) GetRole(ctx context.Context, req *pb.GetRoleRequest) (*pb.GetRoleResponse, error) {
	res, err := amClient.GetRole(ctx, &pbam.GetRoleRequest{
		RoleId: req.RoleId,
	})
	if err != nil {
		return nil, err
	}

	return &pb.GetRoleResponse{
		Role: models.ToPbRole(res.Role),
	}, nil
}

func (p *Server) DescribeRoles(ctx context.Context, req *pb.DescribeRolesRequest) (*pb.DescribeRolesResponse, error) {
	response, err := amClient.DescribeRoles(ctx, &pbam.DescribeRolesRequest{
		SearchWord: req.SearchWord,
		SortKey:    req.SortKey,
		Reverse:    req.Reverse,
		Offset:     req.Offset,
		Limit:      req.Limit,
		RoleId:     req.RoleId,
		RoleName:   req.RoleName,
		Portal:     req.Portal,
		Status:     req.Status,
	})
	if err != nil {
		return nil, err
	}

	var roles []*pb.Role
	for _, role := range response.RoleSet {
		roles = append(roles, models.ToPbRole(role))
	}

	reply := &pb.DescribeRolesResponse{
		TotalCount: response.Total,
		RoleSet:    roles,
	}

	return reply, nil
}

func (p *Server) BindUserRole(ctx context.Context, req *pb.BindUserRoleRequest) (*pb.BindUserRoleResponse, error) {
	_, err := amClient.BindUserRole(ctx, &pbam.BindUserRoleRequest{
		RoleId: req.RoleId,
		UserId: req.UserId,
	})
	if err != nil {
		return nil, err
	}

	return &pb.BindUserRoleResponse{
		UserId: req.UserId,
		RoleId: req.RoleId,
	}, nil
}

func (p *Server) UnbindUserRole(ctx context.Context, req *pb.UnbindUserRoleRequest) (*pb.UnbindUserRoleResponse, error) {
	return nil, gerr.New(ctx, gerr.PermissionDenied, gerr.ErrorPermissionDenied)
}
<commit_msg>Add action bundle id for DescribeRoles in account service<commit_after>// Copyright 2018 The OpenPitrix Authors. All rights reserved.
// Use of this source code is governed by a Apache license
// that can be found in the LICENSE file.

package account

import (
	"context"

	pbam "openpitrix.io/iam/pkg/pb"
	"openpitrix.io/openpitrix/pkg/client/iam/am"
	"openpitrix.io/openpitrix/pkg/gerr"
	"openpitrix.io/openpitrix/pkg/logger"
	"openpitrix.io/openpitrix/pkg/models"
	"openpitrix.io/openpitrix/pkg/pb"
	"openpitrix.io/openpitrix/pkg/util/ctxutil"
	"openpitrix.io/openpitrix/pkg/util/stringutil"
)

var (
	_           pb.AccessManagerServer = (*Server)(nil)
	amClient, _                        = am.NewClient()
)

func validateRoleUser(ctx context.Context, roleId, userId string) bool {
	response, err := amClient.GetRoleWithUser(ctx, &pbam.GetRoleRequest{
		RoleId: roleId,
	})
	if err != nil {
		logger.Error(ctx, "Get role with user failed: %+v", err)
		return false
	}
	if stringutil.StringIn(userId, response.Role.UserIdSet) {
		return true
	} else {
		return false
	}
}

func (p *Server) CanDo(ctx context.Context, req *pb.CanDoRequest) (*pb.CanDoResponse, error) {
	v, err := amClient.CanDo(ctx, &pbam.CanDoRequest{
		UserId:    req.UserId,
		Url:       req.Url,
		UrlMethod: req.UrlMethod,
		ApiMethod: req.ApiMethod,
	})
	if err != nil {
		return nil, err
	}

	return &pb.CanDoResponse{
		UserId:     v.UserId,
		AccessPath: v.AccessPath,
		OwnerPath:  v.OwnerPath,
	}, nil
}

func (p *Server) GetRoleModule(ctx context.Context, req *pb.GetRoleModuleRequest) (*pb.GetRoleModuleResponse, error) {
	response, err := amClient.GetRoleModule(ctx, &pbam.GetRoleModuleRequest{
		RoleId: req.RoleId,
	})
	if err != nil {
		return nil, err
	}

	return &pb.GetRoleModuleResponse{
		RoleId: req.RoleId,
		Module: models.ToPbModule(response.Module),
	}, nil
}

func (p *Server) ModifyRoleModule(ctx context.Context, req *pb.ModifyRoleModuleRequest) (*pb.ModifyRoleModuleResponse, error) {
	_, err := amClient.ModifyRoleModule(ctx, &pbam.ModifyRoleModuleRequest{
		RoleId: req.RoleId,
		Module: models.ToAmModule(req.Module),
	})
	if err != nil {
		return nil, err
	}

	return &pb.ModifyRoleModuleResponse{
		RoleId: req.RoleId,
	}, nil
}

func (p *Server) CreateRole(ctx context.Context, req *pb.CreateRoleRequest) (*pb.CreateRoleResponse, error) {
	s := ctxutil.GetSender(ctx)
	response, err := amClient.CreateRole(ctx, &pbam.CreateRoleRequest{
		RoleName:    req.RoleName,
		Description: req.Description,
		Portal:      req.Portal,
		Owner:       s.GetOwnerPath().Owner(),
		OwnerPath:   string(s.GetOwnerPath()),
	})
	if err != nil {
		return nil, err
	}

	return &pb.CreateRoleResponse{
		RoleId: response.RoleId,
	}, nil
}

func (p *Server) DeleteRoles(ctx context.Context, req *pb.DeleteRolesRequest) (*pb.DeleteRolesResponse, error) {
	_, err := amClient.DeleteRoles(ctx, &pbam.DeleteRolesRequest{
		RoleId: req.RoleId,
	})
	if err != nil {
		return nil, err
	}

	return &pb.DeleteRolesResponse{
		RoleId: req.RoleId,
	}, nil
}

func (p *Server) ModifyRole(ctx context.Context, req *pb.ModifyRoleRequest) (*pb.ModifyRoleResponse, error) {
	_, err := amClient.ModifyRole(ctx, &pbam.ModifyRoleRequest{
		RoleId:      req.RoleId,
		RoleName:    req.RoleName,
		Description: req.Description,
	})
	if err != nil {
		return nil, err
	}

	return &pb.ModifyRoleResponse{
		RoleId: req.RoleId,
	}, nil
}

func (p *Server) GetRole(ctx context.Context, req *pb.GetRoleRequest) (*pb.GetRoleResponse, error) {
	res, err := amClient.GetRole(ctx, &pbam.GetRoleRequest{
		RoleId: req.RoleId,
	})
	if err != nil {
		return nil, err
	}

	return &pb.GetRoleResponse{
		Role: models.ToPbRole(res.Role),
	}, nil
}

func (p *Server) DescribeRoles(ctx context.Context, req *pb.DescribeRolesRequest) (*pb.DescribeRolesResponse, error) {
	response, err := amClient.DescribeRoles(ctx, &pbam.DescribeRolesRequest{
		SearchWord:     req.SearchWord,
		SortKey:        req.SortKey,
		Reverse:        req.Reverse,
		Offset:         req.Offset,
		Limit:          req.Limit,
		RoleId:         req.RoleId,
		RoleName:       req.RoleName,
		Portal:         req.Portal,
		Status:         req.Status,
		ActionBundleId: req.ActionBundleId,
	})
	if err != nil {
		return nil, err
	}

	var roles []*pb.Role
	for _, role := range response.RoleSet {
		roles = append(roles, models.ToPbRole(role))
	}

	reply := &pb.DescribeRolesResponse{
		TotalCount: response.Total,
		RoleSet:    roles,
	}

	return reply, nil
}

func (p *Server) BindUserRole(ctx context.Context, req *pb.BindUserRoleRequest) (*pb.BindUserRoleResponse, error) {
	_, err := amClient.BindUserRole(ctx, &pbam.BindUserRoleRequest{
		RoleId: req.RoleId,
		UserId: req.UserId,
	})
	if err != nil {
		return nil, err
	}

	return &pb.BindUserRoleResponse{
		UserId: req.UserId,
		RoleId: req.RoleId,
	}, nil
}

func (p *Server) UnbindUserRole(ctx context.Context, req *pb.UnbindUserRoleRequest) (*pb.UnbindUserRoleResponse, error) {
	return nil, gerr.New(ctx, gerr.PermissionDenied, gerr.ErrorPermissionDenied)
}
<|endoftext|>


<commit_before>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2017 Red Hat, Inc.
 *
 */

package cmdclient

//go:generate mockgen -source $GOFILE -package=$GOPACKAGE -destination=generated_mock_$GOFILE

/*
 ATTENTION: Rerun code generators when interface signatures are modified.
*/

import (
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/rpc"
	"os"
	"path/filepath"
	"syscall"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	"k8s.io/apimachinery/pkg/api/resource"
	"k8s.io/apimachinery/pkg/util/json"

	v1 "kubevirt.io/client-go/api/v1"
	"kubevirt.io/client-go/log"
	diskutils "kubevirt.io/kubevirt/pkg/ephemeral-disk-utils"
	com "kubevirt.io/kubevirt/pkg/handler-launcher-com"
	"kubevirt.io/kubevirt/pkg/handler-launcher-com/cmd/info"
	cmdv1 "kubevirt.io/kubevirt/pkg/handler-launcher-com/cmd/v1"
	grpcutil "kubevirt.io/kubevirt/pkg/util/net/grpc"
	"kubevirt.io/kubevirt/pkg/virt-launcher/virtwrap/api"
	"kubevirt.io/kubevirt/pkg/virt-launcher/virtwrap/stats"
)

var (
	// add older version when supported
	// don't use the variable in pkg/handler-launcher-com/cmd/v1/version.go in order to detect version mismatches early
	supportedCmdVersions = []uint32{1}
)

type MigrationOptions struct {
	Bandwidth               resource.Quantity
	ProgressTimeout         int64
	CompletionTimeoutPerGiB int64
	UnsafeMigration         bool
	AllowAutoConverge       bool
}

type LauncherClient interface {
	SyncVirtualMachine(vmi *v1.VirtualMachineInstance) error
	SyncMigrationTarget(vmi *v1.VirtualMachineInstance) error
	ShutdownVirtualMachine(vmi *v1.VirtualMachineInstance) error
	KillVirtualMachine(vmi *v1.VirtualMachineInstance) error
	MigrateVirtualMachine(vmi *v1.VirtualMachineInstance, options *MigrationOptions) error
	CancelVirtualMachineMigration(vmi *v1.VirtualMachineInstance) error
	DeleteDomain(vmi *v1.VirtualMachineInstance) error
	GetDomain() (*api.Domain, bool, error)
	GetDomainStats() (*stats.DomainStats, bool, error)
	Ping() error
	Close()
}

type VirtLauncherClient struct {
	v1client cmdv1.CmdClient
	conn     *grpc.ClientConn
}

func ListAllSockets(baseDir string) ([]string, error) {
	var socketFiles []string

	fileDir := filepath.Join(baseDir, "sockets")
	exists, err := diskutils.FileExists(fileDir)
	if err != nil {
		return nil, err
	}

	if exists == false {
		return socketFiles, nil
	}

	files, err := ioutil.ReadDir(fileDir)
	if err != nil {
		return nil, err
	}
	for _, file := range files {
		socketFiles = append(socketFiles, filepath.Join(fileDir, file.Name()))
	}
	return socketFiles, nil
}

func SocketsDirectory(baseDir string) string {
	return filepath.Join(baseDir, "sockets")
}

func SocketFromUID(baseDir string, uid string) string {
	sockFile := uid + "_sock"
	return filepath.Join(SocketsDirectory(baseDir), sockFile)
}

func NewClient(socketPath string) (LauncherClient, error) {
	// dial socket
	conn, err := grpcutil.DialSocket(socketPath)
	if err != nil {
		log.Log.Reason(err).Infof("failed to dial cmd socket: %s", socketPath)
		return nil, err
	}

	// create info client and find cmd version to use
	infoClient := info.NewCmdInfoClient(conn)
	return NewClientWithInfoClient(infoClient, conn)
}

func NewClientWithInfoClient(infoClient info.CmdInfoClient, conn *grpc.ClientConn) (LauncherClient, error) {
	ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
	info, err := infoClient.Info(ctx, &info.CmdInfoRequest{})
	if err != nil {
		return nil, fmt.Errorf("could not check cmd server version: %v", err)
	}
	version, err := com.GetHighestCompatibleVersion(info.SupportedCmdVersions, supportedCmdVersions)
	if err != nil {
		return nil, err
	}

	// create cmd client
	switch version {
	case 1:
		client := cmdv1.NewCmdClient(conn)
		return newV1Client(client, conn), nil
	default:
		return nil, fmt.Errorf("cmd client version %v not implemented yet", version)
	}
}

func newV1Client(client cmdv1.CmdClient, conn *grpc.ClientConn) LauncherClient {
	return &VirtLauncherClient{
		v1client: client,
		conn:     conn,
	}
}

func (c *VirtLauncherClient) Close() {
	c.conn.Close()
}

func (c *VirtLauncherClient) genericSendVMICmd(cmdName string,
	cmdFunc func(ctx context.Context, request *cmdv1.VMIRequest, opts ...grpc.CallOption) (*cmdv1.Response, error),
	vmi *v1.VirtualMachineInstance) error {

	vmiJson, err := json.Marshal(vmi)
	if err != nil {
		return err
	}

	request := &cmdv1.VMIRequest{
		Vmi: &cmdv1.VMI{
			VmiJson: vmiJson,
		},
	}

	ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
	defer cancel()
	response, err := cmdFunc(ctx, request)

	err = handleError(err, cmdName, response)
	return err
}

func handleError(err error, cmdName string, response *cmdv1.Response) error {
	if IsDisconnected(err) {
		return err
	} else if err != nil {
		msg := fmt.Sprintf("unknown error encountered sending command %s: %s", cmdName, err.Error())
		return fmt.Errorf(msg)
	} else if response.Success != true {
		return fmt.Errorf("server error. command %s failed: %q", cmdName, response.Message)
	}
	return nil
}

func IsDisconnected(err error) bool {
	if err == nil {
		return false
	}

	if err == rpc.ErrShutdown || err == io.ErrUnexpectedEOF || err == io.EOF {
		return true
	}

	if opErr, ok := err.(*net.OpError); ok {
		if syscallErr, ok := opErr.Err.(*os.SyscallError); ok {
			// catches "connection reset by peer"
			if syscallErr.Err == syscall.ECONNRESET {
				return true
			}
		}
	}

	if grpcStatus, ok := status.FromError(err); ok {

		// see https://github.com/grpc/grpc-go/blob/master/codes/codes.go
		// TODO which other codes might be related to disconnection...?
		switch grpcStatus.Code() {
		case codes.Canceled:
			// e.g. v1client connection closing
			return true
		}

	}

	return false
}

func (c *VirtLauncherClient) SyncVirtualMachine(vmi *v1.VirtualMachineInstance) error {
	return c.genericSendVMICmd("SyncVMI", c.v1client.SyncVirtualMachine, vmi)

}

func (c *VirtLauncherClient) ShutdownVirtualMachine(vmi *v1.VirtualMachineInstance) error {
	return c.genericSendVMICmd("Shutdown", c.v1client.ShutdownVirtualMachine, vmi)
}

func (c *VirtLauncherClient) KillVirtualMachine(vmi *v1.VirtualMachineInstance) error {
	return c.genericSendVMICmd("Kill", c.v1client.KillVirtualMachine, vmi)
}

func (c *VirtLauncherClient) DeleteDomain(vmi *v1.VirtualMachineInstance) error {
	return c.genericSendVMICmd("Delete", c.v1client.DeleteVirtualMachine, vmi)
}

func (c *VirtLauncherClient) MigrateVirtualMachine(vmi *v1.VirtualMachineInstance, options *MigrationOptions) error {

	vmiJson, err := json.Marshal(vmi)
	if err != nil {
		return err
	}

	optionsJson, err := json.Marshal(options)
	if err != nil {
		return err
	}

	request := &cmdv1.MigrationRequest{
		Vmi: &cmdv1.VMI{
			VmiJson: vmiJson,
		},
		Options: optionsJson,
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	response, err := c.v1client.MigrateVirtualMachine(ctx, request)

	err = handleError(err, "Migrate", response)
	return err

}

func (c *VirtLauncherClient) CancelVirtualMachineMigration(vmi *v1.VirtualMachineInstance) error {
	return c.genericSendVMICmd("CancelMigration", c.v1client.CancelVirtualMachineMigration, vmi)
}

func (c *VirtLauncherClient) SyncMigrationTarget(vmi *v1.VirtualMachineInstance) error {
	return c.genericSendVMICmd("SyncMigrationTarget", c.v1client.SyncMigrationTarget, vmi)

}

func (c *VirtLauncherClient) GetDomain() (*api.Domain, bool, error) {

	domain := &api.Domain{}
	exists := false

	request := &cmdv1.EmptyRequest{}
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	response, err := c.v1client.GetDomain(ctx, request)

	if err = handleError(err, "GetDomain", response.Response); err != nil {
		return domain, exists, err
	}

	if response.Domain != "" {
		if err := json.Unmarshal([]byte(response.Domain), domain); err != nil {
			log.Log.Reason(err).Error("error unmarshalling domain")
			return domain, exists, err
		}
		exists = true
	}
	return domain, exists, nil
}

func (c *VirtLauncherClient) GetDomainStats() (*stats.DomainStats, bool, error) {
	stats := &stats.DomainStats{}
	exists := false

	request := &cmdv1.EmptyRequest{}
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	response, err := c.v1client.GetDomainStats(ctx, request)

	if err = handleError(err, "GetDomainStats", response.Response); err != nil {
		return stats, exists, err
	}

	if response.DomainStats != "" {
		if err := json.Unmarshal([]byte(response.DomainStats), stats); err != nil {
			log.Log.Reason(err).Error("error unmarshalling domain")
			return stats, exists, err
		}
		exists = true
	}
	return stats, exists, nil
}

func (c *VirtLauncherClient) Ping() error {
	request := &cmdv1.EmptyRequest{}
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	response, err := c.v1client.Ping(ctx, request)

	err = handleError(err, "Ping", response)
	return err
}
<commit_msg>Make timeout constant, add long to MigrateVirtualMachine too<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2017 Red Hat, Inc.
 *
 */

package cmdclient

//go:generate mockgen -source $GOFILE -package=$GOPACKAGE -destination=generated_mock_$GOFILE

/*
 ATTENTION: Rerun code generators when interface signatures are modified.
*/

import (
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/rpc"
	"os"
	"path/filepath"
	"syscall"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	"k8s.io/apimachinery/pkg/api/resource"
	"k8s.io/apimachinery/pkg/util/json"

	v1 "kubevirt.io/client-go/api/v1"
	"kubevirt.io/client-go/log"
	diskutils "kubevirt.io/kubevirt/pkg/ephemeral-disk-utils"
	com "kubevirt.io/kubevirt/pkg/handler-launcher-com"
	"kubevirt.io/kubevirt/pkg/handler-launcher-com/cmd/info"
	cmdv1 "kubevirt.io/kubevirt/pkg/handler-launcher-com/cmd/v1"
	grpcutil "kubevirt.io/kubevirt/pkg/util/net/grpc"
	"kubevirt.io/kubevirt/pkg/virt-launcher/virtwrap/api"
	"kubevirt.io/kubevirt/pkg/virt-launcher/virtwrap/stats"
)

var (
	// add older version when supported
	// don't use the variable in pkg/handler-launcher-com/cmd/v1/version.go in order to detect version mismatches early
	supportedCmdVersions = []uint32{1}
)

type MigrationOptions struct {
	Bandwidth               resource.Quantity
	ProgressTimeout         int64
	CompletionTimeoutPerGiB int64
	UnsafeMigration         bool
	AllowAutoConverge       bool
}

type LauncherClient interface {
	SyncVirtualMachine(vmi *v1.VirtualMachineInstance) error
	SyncMigrationTarget(vmi *v1.VirtualMachineInstance) error
	ShutdownVirtualMachine(vmi *v1.VirtualMachineInstance) error
	KillVirtualMachine(vmi *v1.VirtualMachineInstance) error
	MigrateVirtualMachine(vmi *v1.VirtualMachineInstance, options *MigrationOptions) error
	CancelVirtualMachineMigration(vmi *v1.VirtualMachineInstance) error
	DeleteDomain(vmi *v1.VirtualMachineInstance) error
	GetDomain() (*api.Domain, bool, error)
	GetDomainStats() (*stats.DomainStats, bool, error)
	Ping() error
	Close()
}

type VirtLauncherClient struct {
	v1client cmdv1.CmdClient
	conn     *grpc.ClientConn
}

const (
	shortTimeout time.Duration = 5 * time.Second
	longTimeout  time.Duration = 20 * time.Second
)

func ListAllSockets(baseDir string) ([]string, error) {
	var socketFiles []string

	fileDir := filepath.Join(baseDir, "sockets")
	exists, err := diskutils.FileExists(fileDir)
	if err != nil {
		return nil, err
	}

	if exists == false {
		return socketFiles, nil
	}

	files, err := ioutil.ReadDir(fileDir)
	if err != nil {
		return nil, err
	}
	for _, file := range files {
		socketFiles = append(socketFiles, filepath.Join(fileDir, file.Name()))
	}
	return socketFiles, nil
}

func SocketsDirectory(baseDir string) string {
	return filepath.Join(baseDir, "sockets")
}

func SocketFromUID(baseDir string, uid string) string {
	sockFile := uid + "_sock"
	return filepath.Join(SocketsDirectory(baseDir), sockFile)
}

func NewClient(socketPath string) (LauncherClient, error) {
	// dial socket
	conn, err := grpcutil.DialSocket(socketPath)
	if err != nil {
		log.Log.Reason(err).Infof("failed to dial cmd socket: %s", socketPath)
		return nil, err
	}

	// create info client and find cmd version to use
	infoClient := info.NewCmdInfoClient(conn)
	return NewClientWithInfoClient(infoClient, conn)
}

func NewClientWithInfoClient(infoClient info.CmdInfoClient, conn *grpc.ClientConn) (LauncherClient, error) {
	ctx, _ := context.WithTimeout(context.Background(), shortTimeout)
	info, err := infoClient.Info(ctx, &info.CmdInfoRequest{})
	if err != nil {
		return nil, fmt.Errorf("could not check cmd server version: %v", err)
	}
	version, err := com.GetHighestCompatibleVersion(info.SupportedCmdVersions, supportedCmdVersions)
	if err != nil {
		return nil, err
	}

	// create cmd client
	switch version {
	case 1:
		client := cmdv1.NewCmdClient(conn)
		return newV1Client(client, conn), nil
	default:
		return nil, fmt.Errorf("cmd client version %v not implemented yet", version)
	}
}

func newV1Client(client cmdv1.CmdClient, conn *grpc.ClientConn) LauncherClient {
	return &VirtLauncherClient{
		v1client: client,
		conn:     conn,
	}
}

func (c *VirtLauncherClient) Close() {
	c.conn.Close()
}

func (c *VirtLauncherClient) genericSendVMICmd(cmdName string,
	cmdFunc func(ctx context.Context, request *cmdv1.VMIRequest, opts ...grpc.CallOption) (*cmdv1.Response, error),
	vmi *v1.VirtualMachineInstance) error {

	vmiJson, err := json.Marshal(vmi)
	if err != nil {
		return err
	}

	request := &cmdv1.VMIRequest{
		Vmi: &cmdv1.VMI{
			VmiJson: vmiJson,
		},
	}

	ctx, cancel := context.WithTimeout(context.Background(), longTimeout)
	defer cancel()
	response, err := cmdFunc(ctx, request)

	err = handleError(err, cmdName, response)
	return err
}

func handleError(err error, cmdName string, response *cmdv1.Response) error {
	if IsDisconnected(err) {
		return err
	} else if err != nil {
		msg := fmt.Sprintf("unknown error encountered sending command %s: %s", cmdName, err.Error())
		return fmt.Errorf(msg)
	} else if response.Success != true {
		return fmt.Errorf("server error. command %s failed: %q", cmdName, response.Message)
	}
	return nil
}

func IsDisconnected(err error) bool {
	if err == nil {
		return false
	}

	if err == rpc.ErrShutdown || err == io.ErrUnexpectedEOF || err == io.EOF {
		return true
	}

	if opErr, ok := err.(*net.OpError); ok {
		if syscallErr, ok := opErr.Err.(*os.SyscallError); ok {
			// catches "connection reset by peer"
			if syscallErr.Err == syscall.ECONNRESET {
				return true
			}
		}
	}

	if grpcStatus, ok := status.FromError(err); ok {

		// see https://github.com/grpc/grpc-go/blob/master/codes/codes.go
		// TODO which other codes might be related to disconnection...?
		switch grpcStatus.Code() {
		case codes.Canceled:
			// e.g. v1client connection closing
			return true
		}

	}

	return false
}

func (c *VirtLauncherClient) SyncVirtualMachine(vmi *v1.VirtualMachineInstance) error {
	return c.genericSendVMICmd("SyncVMI", c.v1client.SyncVirtualMachine, vmi)

}

func (c *VirtLauncherClient) ShutdownVirtualMachine(vmi *v1.VirtualMachineInstance) error {
	return c.genericSendVMICmd("Shutdown", c.v1client.ShutdownVirtualMachine, vmi)
}

func (c *VirtLauncherClient) KillVirtualMachine(vmi *v1.VirtualMachineInstance) error {
	return c.genericSendVMICmd("Kill", c.v1client.KillVirtualMachine, vmi)
}

func (c *VirtLauncherClient) DeleteDomain(vmi *v1.VirtualMachineInstance) error {
	return c.genericSendVMICmd("Delete", c.v1client.DeleteVirtualMachine, vmi)
}

func (c *VirtLauncherClient) MigrateVirtualMachine(vmi *v1.VirtualMachineInstance, options *MigrationOptions) error {

	vmiJson, err := json.Marshal(vmi)
	if err != nil {
		return err
	}

	optionsJson, err := json.Marshal(options)
	if err != nil {
		return err
	}

	request := &cmdv1.MigrationRequest{
		Vmi: &cmdv1.VMI{
			VmiJson: vmiJson,
		},
		Options: optionsJson,
	}

	ctx, cancel := context.WithTimeout(context.Background(), longTimeout)
	defer cancel()
	response, err := c.v1client.MigrateVirtualMachine(ctx, request)

	err = handleError(err, "Migrate", response)
	return err

}

func (c *VirtLauncherClient) CancelVirtualMachineMigration(vmi *v1.VirtualMachineInstance) error {
	return c.genericSendVMICmd("CancelMigration", c.v1client.CancelVirtualMachineMigration, vmi)
}

func (c *VirtLauncherClient) SyncMigrationTarget(vmi *v1.VirtualMachineInstance) error {
	return c.genericSendVMICmd("SyncMigrationTarget", c.v1client.SyncMigrationTarget, vmi)

}

func (c *VirtLauncherClient) GetDomain() (*api.Domain, bool, error) {

	domain := &api.Domain{}
	exists := false

	request := &cmdv1.EmptyRequest{}
	ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
	defer cancel()
	response, err := c.v1client.GetDomain(ctx, request)

	if err = handleError(err, "GetDomain", response.Response); err != nil {
		return domain, exists, err
	}

	if response.Domain != "" {
		if err := json.Unmarshal([]byte(response.Domain), domain); err != nil {
			log.Log.Reason(err).Error("error unmarshalling domain")
			return domain, exists, err
		}
		exists = true
	}
	return domain, exists, nil
}

func (c *VirtLauncherClient) GetDomainStats() (*stats.DomainStats, bool, error) {
	stats := &stats.DomainStats{}
	exists := false

	request := &cmdv1.EmptyRequest{}
	ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
	defer cancel()
	response, err := c.v1client.GetDomainStats(ctx, request)

	if err = handleError(err, "GetDomainStats", response.Response); err != nil {
		return stats, exists, err
	}

	if response.DomainStats != "" {
		if err := json.Unmarshal([]byte(response.DomainStats), stats); err != nil {
			log.Log.Reason(err).Error("error unmarshalling domain")
			return stats, exists, err
		}
		exists = true
	}
	return stats, exists, nil
}

func (c *VirtLauncherClient) Ping() error {
	request := &cmdv1.EmptyRequest{}
	ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
	defer cancel()
	response, err := c.v1client.Ping(ctx, request)

	err = handleError(err, "Ping", response)
	return err
}
<|endoftext|>


<commit_before>// +build linux

package cpu

import (
	"context"
	"errors"
	"fmt"
	"os/exec"
	"regexp"
	"strconv"
	"strings"

	"github.com/shirou/gopsutil/internal/common"
)

var CPUTick = float64(100)

func init() {
	getconf, err := exec.LookPath("/usr/bin/getconf")
	if err != nil {
		return
	}
	out, err := invoke.CommandWithContext(context.Background(), getconf, "CLK_TCK")
	// ignore errors
	if err == nil {
		i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64)
		if err == nil {
			CPUTick = i
		}
	}
}

func Times(percpu bool) ([]TimesStat, error) {
	return TimesWithContext(context.Background(), percpu)
}

func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
	filename := common.HostProc("stat")
	var lines = []string{}
	if percpu {
		statlines, err := common.ReadLines(filename)
		if err != nil || len(statlines) < 2 {
			return []TimesStat{}, nil
		}
		for _, line := range statlines[1:] {
			if !strings.HasPrefix(line, "cpu") {
				break
			}
			lines = append(lines, line)
		}
	} else {
		lines, _ = common.ReadLinesOffsetN(filename, 0, 1)
	}

	ret := make([]TimesStat, 0, len(lines))

	for _, line := range lines {
		ct, err := parseStatLine(line)
		if err != nil {
			continue
		}
		ret = append(ret, *ct)

	}
	return ret, nil
}

func sysCPUPath(cpu int32, relPath string) string {
	return common.HostSys(fmt.Sprintf("devices/system/cpu/cpu%d", cpu), relPath)
}

func finishCPUInfo(c *InfoStat) error {
	var lines []string
	var err error
	var value float64

	if len(c.CoreID) == 0 {
		lines, err = common.ReadLines(sysCPUPath(c.CPU, "topology/core_id"))
		if err == nil {
			c.CoreID = lines[0]
		}
	}

	// override the value of c.Mhz with cpufreq/cpuinfo_max_freq regardless
	// of the value from /proc/cpuinfo because we want to report the maximum
	// clock-speed of the CPU for c.Mhz, matching the behaviour of Windows
	lines, err = common.ReadLines(sysCPUPath(c.CPU, "cpufreq/cpuinfo_max_freq"))
	// if we encounter errors below such as there are no cpuinfo_max_freq file,
	// we just ignore. so let Mhz is 0.
	if err != nil {
		return nil
	}
	value, err = strconv.ParseFloat(lines[0], 64)
	if err != nil {
		return nil
	}
	c.Mhz = value / 1000.0 // value is in kHz
	if c.Mhz > 9999 {
		c.Mhz = c.Mhz / 1000.0 // value in Hz
	}
	return nil
}

// CPUInfo on linux will return 1 item per physical thread.
//
// CPUs have three levels of counting: sockets, cores, threads.
// Cores with HyperThreading count as having 2 threads per core.
// Sockets often come with many physical CPU cores.
// For example a single socket board with two cores each with HT will
// return 4 CPUInfoStat structs on Linux and the "Cores" field set to 1.
func Info() ([]InfoStat, error) {
	return InfoWithContext(context.Background())
}

func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
	filename := common.HostProc("cpuinfo")
	lines, _ := common.ReadLines(filename)

	var ret []InfoStat
	var processorName string

	c := InfoStat{CPU: -1, Cores: 1}
	for _, line := range lines {
		fields := strings.Split(line, ":")
		if len(fields) < 2 {
			continue
		}
		key := strings.TrimSpace(fields[0])
		value := strings.TrimSpace(fields[1])

		switch key {
		case "Processor":
			processorName = value
		case "processor":
			if c.CPU >= 0 {
				err := finishCPUInfo(&c)
				if err != nil {
					return ret, err
				}
				ret = append(ret, c)
			}
			c = InfoStat{Cores: 1, ModelName: processorName}
			t, err := strconv.ParseInt(value, 10, 64)
			if err != nil {
				return ret, err
			}
			c.CPU = int32(t)
		case "vendorId", "vendor_id":
			c.VendorID = value
		case "cpu family":
			c.Family = value
		case "model":
			c.Model = value
		case "model name", "cpu":
			c.ModelName = value
			if strings.Contains(value, "POWER8") ||
				strings.Contains(value, "POWER7") {
				c.Model = strings.Split(value, " ")[0]
				c.Family = "POWER"
				c.VendorID = "IBM"
			}
		case "stepping", "revision":
			val := value

			if key == "revision" {
				val = strings.Split(value, ".")[0]
			}

			t, err := strconv.ParseInt(val, 10, 64)
			if err != nil {
				return ret, err
			}
			c.Stepping = int32(t)
		case "cpu MHz", "clock":
			// treat this as the fallback value, thus we ignore error
			if t, err := strconv.ParseFloat(strings.Replace(value, "MHz", "", 1), 64); err == nil {
				c.Mhz = t
			}
		case "cache size":
			t, err := strconv.ParseInt(strings.Replace(value, " KB", "", 1), 10, 64)
			if err != nil {
				return ret, err
			}
			c.CacheSize = int32(t)
		case "physical id":
			c.PhysicalID = value
		case "core id":
			c.CoreID = value
		case "flags", "Features":
			c.Flags = strings.FieldsFunc(value, func(r rune) bool {
				return r == ',' || r == ' '
			})
		case "microcode":
			c.Microcode = value
		}
	}
	if c.CPU >= 0 {
		err := finishCPUInfo(&c)
		if err != nil {
			return ret, err
		}
		ret = append(ret, c)
	}
	return ret, nil
}

func parseStatLine(line string) (*TimesStat, error) {
	fields := strings.Fields(line)

	if len(fields) == 0 {
		return nil, errors.New("stat does not contain cpu info")
	}

	if strings.HasPrefix(fields[0], "cpu") == false {
		return nil, errors.New("not contain cpu")
	}

	cpu := fields[0]
	if cpu == "cpu" {
		cpu = "cpu-total"
	}
	user, err := strconv.ParseFloat(fields[1], 64)
	if err != nil {
		return nil, err
	}
	nice, err := strconv.ParseFloat(fields[2], 64)
	if err != nil {
		return nil, err
	}
	system, err := strconv.ParseFloat(fields[3], 64)
	if err != nil {
		return nil, err
	}
	idle, err := strconv.ParseFloat(fields[4], 64)
	if err != nil {
		return nil, err
	}
	iowait, err := strconv.ParseFloat(fields[5], 64)
	if err != nil {
		return nil, err
	}
	irq, err := strconv.ParseFloat(fields[6], 64)
	if err != nil {
		return nil, err
	}
	softirq, err := strconv.ParseFloat(fields[7], 64)
	if err != nil {
		return nil, err
	}

	ct := &TimesStat{
		CPU:     cpu,
		User:    user / CPUTick,
		Nice:    nice / CPUTick,
		System:  system / CPUTick,
		Idle:    idle / CPUTick,
		Iowait:  iowait / CPUTick,
		Irq:     irq / CPUTick,
		Softirq: softirq / CPUTick,
	}
	if len(fields) > 8 { // Linux >= 2.6.11
		steal, err := strconv.ParseFloat(fields[8], 64)
		if err != nil {
			return nil, err
		}
		ct.Steal = steal / CPUTick
	}
	if len(fields) > 9 { // Linux >= 2.6.24
		guest, err := strconv.ParseFloat(fields[9], 64)
		if err != nil {
			return nil, err
		}
		ct.Guest = guest / CPUTick
	}
	if len(fields) > 10 { // Linux >= 3.2.0
		guestNice, err := strconv.ParseFloat(fields[10], 64)
		if err != nil {
			return nil, err
		}
		ct.GuestNice = guestNice / CPUTick
	}

	return ct, nil
}

func CountsWithContext(ctx context.Context, logical bool) (int, error) {
	if logical {
		ret := 0
		// https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_pslinux.py#L599
		procCpuinfo := common.HostProc("cpuinfo")
		lines, err := common.ReadLines(procCpuinfo)
		if err == nil {
			for _, line := range lines {
				line = strings.ToLower(line)
				if strings.HasPrefix(line, "processor") {
					ret++
				}
			}
		}
		if ret == 0 {
			procStat := common.HostProc("stat")
			lines, err = common.ReadLines(procStat)
			if err != nil {
				return 0, err
			}
			re := regexp.MustCompile(`cpu\d`)
			for _, line := range lines {
				line = strings.Split(line, " ")[0]
				if re.MatchString(line) {
					ret++
				}
			}
		}
		return ret, nil
	}
	// physical cores https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_pslinux.py#L628
	filename := common.HostProc("cpuinfo")
	lines, err := common.ReadLines(filename)
	if err != nil {
		return 0, err
	}
	mapping := make(map[int]int)
	currentInfo := make(map[string]int)
	for _, line := range lines {
		line = strings.ToLower(strings.TrimSpace(line))
		if line == "" {
			// new section
			id, okID := currentInfo["physical id"]
			cores, okCores := currentInfo["cpu cores"]
			if okID && okCores {
				mapping[id] = cores
			}
			currentInfo = make(map[string]int)
			continue
		}
		fields := strings.Split(line, ":")
		if len(fields) < 2 {
			continue
		}
		fields[0] = strings.TrimSpace(fields[0])
		if fields[0] == "physical id" || fields[0] == "cpu cores" {
			val, err := strconv.Atoi(strings.TrimSpace(fields[1]))
			if err != nil {
				fmt.Println(err)
				continue
			}
			currentInfo[fields[0]] = val
		}
	}
	ret := 0
	for _, v := range mapping {
		ret += v
	}
	return ret, nil
}
<commit_msg>[cpu][linux] Remove regexp in Counts<commit_after>// +build linux

package cpu

import (
	"context"
	"errors"
	"fmt"
	"os/exec"
	"strconv"
	"strings"

	"github.com/shirou/gopsutil/internal/common"
)

var CPUTick = float64(100)

func init() {
	getconf, err := exec.LookPath("/usr/bin/getconf")
	if err != nil {
		return
	}
	out, err := invoke.CommandWithContext(context.Background(), getconf, "CLK_TCK")
	// ignore errors
	if err == nil {
		i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64)
		if err == nil {
			CPUTick = i
		}
	}
}

func Times(percpu bool) ([]TimesStat, error) {
	return TimesWithContext(context.Background(), percpu)
}

func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
	filename := common.HostProc("stat")
	var lines = []string{}
	if percpu {
		statlines, err := common.ReadLines(filename)
		if err != nil || len(statlines) < 2 {
			return []TimesStat{}, nil
		}
		for _, line := range statlines[1:] {
			if !strings.HasPrefix(line, "cpu") {
				break
			}
			lines = append(lines, line)
		}
	} else {
		lines, _ = common.ReadLinesOffsetN(filename, 0, 1)
	}

	ret := make([]TimesStat, 0, len(lines))

	for _, line := range lines {
		ct, err := parseStatLine(line)
		if err != nil {
			continue
		}
		ret = append(ret, *ct)

	}
	return ret, nil
}

func sysCPUPath(cpu int32, relPath string) string {
	return common.HostSys(fmt.Sprintf("devices/system/cpu/cpu%d", cpu), relPath)
}

func finishCPUInfo(c *InfoStat) error {
	var lines []string
	var err error
	var value float64

	if len(c.CoreID) == 0 {
		lines, err = common.ReadLines(sysCPUPath(c.CPU, "topology/core_id"))
		if err == nil {
			c.CoreID = lines[0]
		}
	}

	// override the value of c.Mhz with cpufreq/cpuinfo_max_freq regardless
	// of the value from /proc/cpuinfo because we want to report the maximum
	// clock-speed of the CPU for c.Mhz, matching the behaviour of Windows
	lines, err = common.ReadLines(sysCPUPath(c.CPU, "cpufreq/cpuinfo_max_freq"))
	// if we encounter errors below such as there are no cpuinfo_max_freq file,
	// we just ignore. so let Mhz is 0.
	if err != nil {
		return nil
	}
	value, err = strconv.ParseFloat(lines[0], 64)
	if err != nil {
		return nil
	}
	c.Mhz = value / 1000.0 // value is in kHz
	if c.Mhz > 9999 {
		c.Mhz = c.Mhz / 1000.0 // value in Hz
	}
	return nil
}

// CPUInfo on linux will return 1 item per physical thread.
//
// CPUs have three levels of counting: sockets, cores, threads.
// Cores with HyperThreading count as having 2 threads per core.
// Sockets often come with many physical CPU cores.
// For example a single socket board with two cores each with HT will
// return 4 CPUInfoStat structs on Linux and the "Cores" field set to 1.
func Info() ([]InfoStat, error) {
	return InfoWithContext(context.Background())
}

func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
	filename := common.HostProc("cpuinfo")
	lines, _ := common.ReadLines(filename)

	var ret []InfoStat
	var processorName string

	c := InfoStat{CPU: -1, Cores: 1}
	for _, line := range lines {
		fields := strings.Split(line, ":")
		if len(fields) < 2 {
			continue
		}
		key := strings.TrimSpace(fields[0])
		value := strings.TrimSpace(fields[1])

		switch key {
		case "Processor":
			processorName = value
		case "processor":
			if c.CPU >= 0 {
				err := finishCPUInfo(&c)
				if err != nil {
					return ret, err
				}
				ret = append(ret, c)
			}
			c = InfoStat{Cores: 1, ModelName: processorName}
			t, err := strconv.ParseInt(value, 10, 64)
			if err != nil {
				return ret, err
			}
			c.CPU = int32(t)
		case "vendorId", "vendor_id":
			c.VendorID = value
		case "cpu family":
			c.Family = value
		case "model":
			c.Model = value
		case "model name", "cpu":
			c.ModelName = value
			if strings.Contains(value, "POWER8") ||
				strings.Contains(value, "POWER7") {
				c.Model = strings.Split(value, " ")[0]
				c.Family = "POWER"
				c.VendorID = "IBM"
			}
		case "stepping", "revision":
			val := value

			if key == "revision" {
				val = strings.Split(value, ".")[0]
			}

			t, err := strconv.ParseInt(val, 10, 64)
			if err != nil {
				return ret, err
			}
			c.Stepping = int32(t)
		case "cpu MHz", "clock":
			// treat this as the fallback value, thus we ignore error
			if t, err := strconv.ParseFloat(strings.Replace(value, "MHz", "", 1), 64); err == nil {
				c.Mhz = t
			}
		case "cache size":
			t, err := strconv.ParseInt(strings.Replace(value, " KB", "", 1), 10, 64)
			if err != nil {
				return ret, err
			}
			c.CacheSize = int32(t)
		case "physical id":
			c.PhysicalID = value
		case "core id":
			c.CoreID = value
		case "flags", "Features":
			c.Flags = strings.FieldsFunc(value, func(r rune) bool {
				return r == ',' || r == ' '
			})
		case "microcode":
			c.Microcode = value
		}
	}
	if c.CPU >= 0 {
		err := finishCPUInfo(&c)
		if err != nil {
			return ret, err
		}
		ret = append(ret, c)
	}
	return ret, nil
}

func parseStatLine(line string) (*TimesStat, error) {
	fields := strings.Fields(line)

	if len(fields) == 0 {
		return nil, errors.New("stat does not contain cpu info")
	}

	if strings.HasPrefix(fields[0], "cpu") == false {
		return nil, errors.New("not contain cpu")
	}

	cpu := fields[0]
	if cpu == "cpu" {
		cpu = "cpu-total"
	}
	user, err := strconv.ParseFloat(fields[1], 64)
	if err != nil {
		return nil, err
	}
	nice, err := strconv.ParseFloat(fields[2], 64)
	if err != nil {
		return nil, err
	}
	system, err := strconv.ParseFloat(fields[3], 64)
	if err != nil {
		return nil, err
	}
	idle, err := strconv.ParseFloat(fields[4], 64)
	if err != nil {
		return nil, err
	}
	iowait, err := strconv.ParseFloat(fields[5], 64)
	if err != nil {
		return nil, err
	}
	irq, err := strconv.ParseFloat(fields[6], 64)
	if err != nil {
		return nil, err
	}
	softirq, err := strconv.ParseFloat(fields[7], 64)
	if err != nil {
		return nil, err
	}

	ct := &TimesStat{
		CPU:     cpu,
		User:    user / CPUTick,
		Nice:    nice / CPUTick,
		System:  system / CPUTick,
		Idle:    idle / CPUTick,
		Iowait:  iowait / CPUTick,
		Irq:     irq / CPUTick,
		Softirq: softirq / CPUTick,
	}
	if len(fields) > 8 { // Linux >= 2.6.11
		steal, err := strconv.ParseFloat(fields[8], 64)
		if err != nil {
			return nil, err
		}
		ct.Steal = steal / CPUTick
	}
	if len(fields) > 9 { // Linux >= 2.6.24
		guest, err := strconv.ParseFloat(fields[9], 64)
		if err != nil {
			return nil, err
		}
		ct.Guest = guest / CPUTick
	}
	if len(fields) > 10 { // Linux >= 3.2.0
		guestNice, err := strconv.ParseFloat(fields[10], 64)
		if err != nil {
			return nil, err
		}
		ct.GuestNice = guestNice / CPUTick
	}

	return ct, nil
}

func CountsWithContext(ctx context.Context, logical bool) (int, error) {
	if logical {
		ret := 0
		// https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_pslinux.py#L599
		procCpuinfo := common.HostProc("cpuinfo")
		lines, err := common.ReadLines(procCpuinfo)
		if err == nil {
			for _, line := range lines {
				line = strings.ToLower(line)
				if strings.HasPrefix(line, "processor") {
					ret++
				}
			}
		}
		if ret == 0 {
			procStat := common.HostProc("stat")
			lines, err = common.ReadLines(procStat)
			if err != nil {
				return 0, err
			}
			for _, line := range lines {
				if len(line) >= 4 && strings.HasPrefix(line, "cpu") && '0' <= line[3] && line[3] <= '9' { // `^cpu\d` regexp matching
					ret++
				}
			}
		}
		return ret, nil
	}
	// physical cores https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_pslinux.py#L628
	filename := common.HostProc("cpuinfo")
	lines, err := common.ReadLines(filename)
	if err != nil {
		return 0, err
	}
	mapping := make(map[int]int)
	currentInfo := make(map[string]int)
	for _, line := range lines {
		line = strings.ToLower(strings.TrimSpace(line))
		if line == "" {
			// new section
			id, okID := currentInfo["physical id"]
			cores, okCores := currentInfo["cpu cores"]
			if okID && okCores {
				mapping[id] = cores
			}
			currentInfo = make(map[string]int)
			continue
		}
		fields := strings.Split(line, ":")
		if len(fields) < 2 {
			continue
		}
		fields[0] = strings.TrimSpace(fields[0])
		if fields[0] == "physical id" || fields[0] == "cpu cores" {
			val, err := strconv.Atoi(strings.TrimSpace(fields[1]))
			if err != nil {
				continue
			}
			currentInfo[fields[0]] = val
		}
	}
	ret := 0
	for _, v := range mapping {
		ret += v
	}
	return ret, nil
}
<|endoftext|>


<commit_before>package store

import (
	"fmt"
	raftstore "github.com/Dataman-Cloud/swan/src/manager/raft/store"
	"github.com/Dataman-Cloud/swan/src/manager/raft/types"
	"github.com/boltdb/bolt"

	"golang.org/x/net/context"
)

func (s *FrameworkStore) CreateOfferAllocatorItem(ctx context.Context, item *types.OfferAllocatorItem, cb func()) error {
	storeActions := []*types.StoreAction{&types.StoreAction{
		Action: types.StoreActionKindCreate,
		Target: &types.StoreAction_OfferAllocatorItem{item},
	}}

	return s.RaftNode.ProposeValue(ctx, storeActions, cb)
}

func (s *FrameworkStore) ListOfferallocatorItems() ([]*types.OfferAllocatorItem, error) {
	var items []*types.OfferAllocatorItem

	if err := s.BoltbDb.View(func(tx *bolt.Tx) error {
		bkt := raftstore.GetOfferAllocatorItemBucket(tx)
		if bkt == nil {
			items = []*types.OfferAllocatorItem{}
			return nil
		}

		return bkt.ForEach(func(k, v []byte) error {
			item := &types.OfferAllocatorItem{}
			if err := item.Unmarshal(v); err != nil {
				return err
			}

			items = append(items, item)
			return nil
		})

	}); err != nil {
		return nil, err
	}

	fmt.Println("xxxxxxxxxxxxxxxxxxxxxxxxxxxx000000000000")
	fmt.Println(items)

	return items, nil
}

func (s *FrameworkStore) DeleteOfferAllocatorItem(ctx context.Context, slotId string, cb func()) error {
	removeOfferAllocatorItem := &types.OfferAllocatorItem{SlotId: slotId}
	storeActions := []*types.StoreAction{&types.StoreAction{
		Action: types.StoreActionKindRemove,
		Target: &types.StoreAction_OfferAllocatorItem{removeOfferAllocatorItem},
	}}

	return s.RaftNode.ProposeValue(ctx, storeActions, cb)
}
<commit_msg>remove debug print<commit_after>package store

import (
	raftstore "github.com/Dataman-Cloud/swan/src/manager/raft/store"
	"github.com/Dataman-Cloud/swan/src/manager/raft/types"
	"github.com/boltdb/bolt"

	"golang.org/x/net/context"
)

func (s *FrameworkStore) CreateOfferAllocatorItem(ctx context.Context, item *types.OfferAllocatorItem, cb func()) error {
	storeActions := []*types.StoreAction{&types.StoreAction{
		Action: types.StoreActionKindCreate,
		Target: &types.StoreAction_OfferAllocatorItem{item},
	}}

	return s.RaftNode.ProposeValue(ctx, storeActions, cb)
}

func (s *FrameworkStore) ListOfferallocatorItems() ([]*types.OfferAllocatorItem, error) {
	var items []*types.OfferAllocatorItem

	if err := s.BoltbDb.View(func(tx *bolt.Tx) error {
		bkt := raftstore.GetOfferAllocatorItemBucket(tx)
		if bkt == nil {
			items = []*types.OfferAllocatorItem{}
			return nil
		}

		return bkt.ForEach(func(k, v []byte) error {
			item := &types.OfferAllocatorItem{}
			if err := item.Unmarshal(v); err != nil {
				return err
			}

			items = append(items, item)
			return nil
		})

	}); err != nil {
		return nil, err
	}

	return items, nil
}

func (s *FrameworkStore) DeleteOfferAllocatorItem(ctx context.Context, slotId string, cb func()) error {
	removeOfferAllocatorItem := &types.OfferAllocatorItem{SlotId: slotId}
	storeActions := []*types.StoreAction{&types.StoreAction{
		Action: types.StoreActionKindRemove,
		Target: &types.StoreAction_OfferAllocatorItem{removeOfferAllocatorItem},
	}}

	return s.RaftNode.ProposeValue(ctx, storeActions, cb)
}
<|endoftext|>


<commit_before>package codegen

import (
	"errors"
	"go/build"
	"go/format"
	"log"
	"strings"
	"unicode"

	"github.com/MarcGrol/golangAnnotations/model"
	"github.com/MarcGrol/golangAnnotations/parser"
	"github.com/jkomoros/boardgame"
)

type memoizedEmbeddedStructKey struct {
	Import           string
	TargetStructName string
}

var memoizedEmbeddedStructs map[memoizedEmbeddedStructKey]*typeInfo

type typeInfo struct {
	Types       map[string]boardgame.PropertyType
	Mutable     map[string]bool
	UpConverter map[string]string
}

const magicDocLinePrefix = "boardgame:codegen"

func init() {
	memoizedEmbeddedStructs = make(map[memoizedEmbeddedStructKey]*typeInfo)
}

/*

ProcessReaders operates on the package at the given relative location, and
produces two strings, one that is appropriate to be saved in auto_reader.go,
and one that is appropriate to be saved in auto_reader_test.go.

ProcessReaders processes a package of go files, searching for structs that
have a comment immediately above their declaration that begins with
"boardgame:codegen". For each such struct, it creates a Reader(), ReadSetter(),
and ReadSetConfigurer() method that implement boardgame.Reader,
boardgame.ReadSetter, and boardgame.ReadSetConfigurer, respectively.

Producing a ReadSetConfigurator requires a ReadSetter, and producing a
ReadSetter requires a Reader. By default if you have the magic comment of
`boardgame:codegen` it with produce all three. However, if you want only some of
the methods, include an argument for the highest one you want, e.g.
`boardgame:codegen readsetter` to generate a Reader() and ReadSetter().

This package will automatically create additional type transform methods
to handle fields whose literal type is boardgame.ImmutableSizedStack,
boardgame.SizedStack, boardgame.MergedStack, enum.RangeValue, and
enum.TreeValue.

The outputted readers, readsetters, and readsetconfigurers use a hard-
coded list of fields for performance (reflection would be about 30% slower
under normal usage). You should re-generate output every time you add a
struct or modify the fields on a struct.

*/
func ProcessReaders(location string) (output string, testOutput string, err error) {

	sources, err := parser.ParseSourceDir(location, ".*")

	if err != nil {
		return "", "", errors.New("Couldn't parse sources: " + err.Error())
	}

	output, err = doProcessStructs(sources, location, false)

	if err != nil {
		return "", "", errors.New("Couldn't process non-test files: " + err.Error())
	}

	testOutput, err = doProcessStructs(sources, location, true)

	if err != nil {
		return "", "", errors.New("Couldn't process test files: " + err.Error())
	}

	formattedBytes, err := format.Source([]byte(output))

	if err != nil {
		if debugSaveBadCode {
			formattedBytes = []byte(output)
		} else {
			return "", "", errors.New("Couldn't go fmt code for reader: " + err.Error())
		}
	}

	formattedTestBytes, err := format.Source([]byte(testOutput))

	if err != nil {
		if debugSaveBadCode {
			formattedTestBytes = []byte(testOutput)
		} else {
			return "", "", errors.New("Couldn't go fmt code for test reader: " + err.Error())
		}
	}

	return string(formattedBytes), string(formattedTestBytes), nil
}

func doProcessStructs(sources model.ParsedSources, location string, testFiles bool) (output string, err error) {

	if len(sources.Structs) == 0 {
		//If there are no structs at all that's OK, just don't ouput anything.
		return "", nil
	}

	for _, theStruct := range sources.Structs {

		//Only process structs in test or not test files, depending on which mode we're in.
		structInTestFile := strings.HasSuffix(theStruct.Filename, "_test.go")

		if structInTestFile != testFiles {
			continue
		}

		generator := newReaderGenerator(theStruct, location, sources.Structs)

		if generator == nil {
			//No utput necessary
			continue
		}

		output += generator.Output()
	}

	if output != "" {
		output = headerForPackage(sources.Structs[0].PackageName) + output
	}

	return output, nil
}

func fieldNamePublic(name string) bool {
	if len(name) < 1 {
		return false
	}

	firstChar := []rune(name)[0]

	if firstChar != unicode.ToUpper(firstChar) {
		//It was not upper case, thus private, thus should not be included.
		return false
	}

	//TODO: check if the struct says propertyreader:omit

	return true
}

//fieldNamePossibleEmbeddedStruct returns true if it's possible that the field
//is an embedded struct.
func fieldNamePossibleEmbeddedStruct(theField model.Field) bool {

	theTypeParts := strings.Split(theField.TypeName, ".")

	if len(theTypeParts) != 2 {
		return false
	}

	if theField.Name == theTypeParts[0] {
		return true
	}

	return false
}

func structTypes(location string, theStruct model.Struct, allStructs []model.Struct) *typeInfo {

	result := &typeInfo{
		make(map[string]boardgame.PropertyType),
		make(map[string]bool),
		make(map[string]string),
	}

	for _, field := range theStruct.Fields {
		if fieldNamePossibleEmbeddedStruct(field) {
			embeddedInfo := typesForPossibleEmbeddedStruct(location, field, allStructs)
			if embeddedInfo != nil {
				for key, val := range embeddedInfo.Types {
					result.Types[key] = val
				}
				for key, val := range embeddedInfo.Mutable {
					result.Mutable[key] = val
				}
				continue
			}
		}
		//Check if it's a local-to-package anonymous embedded struct
		if field.Name == "" {
			foundStruct := false
			for _, otherStruct := range allStructs {
				if otherStruct.Name == field.TypeName {
					embeddedInfo := structTypes(location, otherStruct, allStructs)
					for key, val := range embeddedInfo.Types {
						result.Types[key] = val
					}
					for key, val := range embeddedInfo.Mutable {
						result.Mutable[key] = val
					}
					foundStruct = true
					break
				}
			}
			if foundStruct {
				continue
			}
		}
		if !fieldNamePublic(field.Name) {
			continue
		}
		switch field.TypeName {
		case "int":
			if field.IsSlice {
				result.Types[field.Name] = boardgame.TypeIntSlice
			} else {
				result.Types[field.Name] = boardgame.TypeInt
			}
			result.Mutable[field.Name] = true
		case "bool":
			if field.IsSlice {
				result.Types[field.Name] = boardgame.TypeBoolSlice
			} else {
				result.Types[field.Name] = boardgame.TypeBool
			}
			result.Mutable[field.Name] = true
		case "string":
			if field.IsSlice {
				result.Types[field.Name] = boardgame.TypeStringSlice
			} else {
				result.Types[field.Name] = boardgame.TypeString
			}
			result.Mutable[field.Name] = true
		case "boardgame.PlayerIndex":
			if field.IsSlice {
				result.Types[field.Name] = boardgame.TypePlayerIndexSlice
			} else {
				result.Types[field.Name] = boardgame.TypePlayerIndex
			}
			result.Mutable[field.Name] = true
		case "boardgame.ImmutableStack":
			result.Types[field.Name] = boardgame.TypeStack
			result.Mutable[field.Name] = false
		case "boardgame.MergedStack":
			result.Types[field.Name] = boardgame.TypeStack
			result.Mutable[field.Name] = false
			result.UpConverter[field.Name] = "MergedStack"
		case "boardgame.Stack":
			result.Types[field.Name] = boardgame.TypeStack
			result.Mutable[field.Name] = true
		case "boardgame.ImmutableSizedStack":
			result.Types[field.Name] = boardgame.TypeStack
			result.Mutable[field.Name] = false
			result.UpConverter[field.Name] = "ImmutableSizedStack"
		case "boardgame.SizedStack":
			result.Types[field.Name] = boardgame.TypeStack
			result.Mutable[field.Name] = true
			result.UpConverter[field.Name] = "SizedStack"
		case "boardgame.ImmutableBoard":
			result.Types[field.Name] = boardgame.TypeBoard
			result.Mutable[field.Name] = false
		case "boardgame.Board":
			result.Types[field.Name] = boardgame.TypeBoard
			result.Mutable[field.Name] = true
		case "enum.ImmutableVal":
			result.Types[field.Name] = boardgame.TypeEnum
			result.Mutable[field.Name] = false
		case "enum.Val":
			result.Types[field.Name] = boardgame.TypeEnum
			result.Mutable[field.Name] = true
		case "enum.ImmutableRangeVal":
			result.Types[field.Name] = boardgame.TypeEnum
			result.Mutable[field.Name] = false
			result.UpConverter[field.Name] = "ImmutableRangeVal"
		case "enum.RangeVal":
			result.Types[field.Name] = boardgame.TypeEnum
			result.Mutable[field.Name] = true
			result.UpConverter[field.Name] = "RangeVal"
		case "enum.ImmutableTreeVal":
			result.Types[field.Name] = boardgame.TypeEnum
			result.Mutable[field.Name] = false
			result.UpConverter[field.Name] = "ImmutableTreeVal"
		case "enum.TreeVal":
			result.Types[field.Name] = boardgame.TypeEnum
			result.Mutable[field.Name] = true
			result.UpConverter[field.Name] = "TreeVal"
		case "boardgame.ImmutableTimer":
			result.Types[field.Name] = boardgame.TypeTimer
			result.Mutable[field.Name] = false
		case "boardgame.Timer":
			result.Types[field.Name] = boardgame.TypeTimer
			result.Mutable[field.Name] = true
		default:
			log.Println("Unknown type on " + theStruct.Name + ": " + field.Name + ": " + field.TypeName)
		}
	}

	return result
}

//typeforPossibleEmbeddedStruct should be called when we think that an unknown
//field MIGHT be an embedded struct. If it is, we will identify the package it
//appears to be built from, parse those structs, try to find the struct, and
//return a map of property types in it.
func typesForPossibleEmbeddedStruct(location string, theField model.Field, allStructs []model.Struct) *typeInfo {

	targetTypeParts := strings.Split(theField.TypeName, ".")

	if len(targetTypeParts) != 2 {
		return nil
	}

	targetType := targetTypeParts[1]

	//base.SubState will be anonymously embedded but should be ignored.
	if targetTypeParts[0] == "base" && targetType == "SubState" {
		return nil
	}

	key := memoizedEmbeddedStructKey{
		Import:           theField.PackageName,
		TargetStructName: targetType,
	}

	foundTypes := memoizedEmbeddedStructs[key]

	if foundTypes != nil {
		return foundTypes
	}

	pkg, err := build.Import(theField.PackageName, location, build.FindOnly)

	if err != nil {
		log.Println("Couldn't find canonical import: " + err.Error())
		return nil
	}

	importPath := pkg.Dir

	sources, err := parser.ParseSourceDir(importPath, ".*")

	if err != nil {
		log.Println("Error in sources for ", theField, err.Error())
		return nil
	}

	for _, theStruct := range sources.Structs {
		if theStruct.Name != targetType {
			continue
		}
		//Found it!
		foundTypes = structTypes(location, theStruct, allStructs)

		memoizedEmbeddedStructs[key] = foundTypes

	}

	return foundTypes

}

func structConfig(docLines []string) (outputReader bool, outputReadSetter bool, outputReadSetConfigurer bool) {

	for _, mainDocLine := range docLines {
		//Multi-line comments will come in as one docline.
		for _, docLine := range strings.Split(mainDocLine, "\n") {
			docLine = strings.ToLower(docLine)
			docLine = strings.TrimPrefix(docLine, "//")
			docLine = strings.TrimSpace(docLine)
			if !strings.HasPrefix(docLine, magicDocLinePrefix) {
				continue
			}
			docLine = strings.TrimPrefix(docLine, magicDocLinePrefix)
			docLine = strings.TrimSpace(docLine)

			switch docLine {
			case "", "all", "readsetconfigurer":
				return true, true, true
			case "readsetter":
				return true, true, false
			case "reader":
				return true, false, false
			}
		}
	}
	return false, false, false
}

func headerForPackage(packageName string) string {

	return templateOutput(headerTemplate, map[string]string{
		"packageName": packageName,
	}) + importText
}

type nameForTypeInfo struct {
	Name        string
	Mutable     bool
	UpConverter string
}

//readerStructName returns the name of the auto-generated reader struct for the
//given struct.
func readerStructName(structName string) string {
	//The prefix used to be "__" but that didn't lint correctly, so instead use
	//a non-latin prefix character that is like an a but with a dot (to make it
	//less likely to show up in autocompletes in IDEs)
	return "ȧutoGenerated" + strings.Title(structName) + "Reader"
}

func readerForStruct(structName string) string {

	return templateOutput(readerTemplate, map[string]string{
		"firstLetter": strings.ToLower(structName[:1]),
		"structName":  structName,
		"readerName":  readerStructName(structName),
	})

}

func readSetterForStruct(structName string) string {

	return templateOutput(readSetterTemplate, map[string]string{
		"firstLetter": strings.ToLower(structName[:1]),
		"structName":  structName,
		"readerName":  readerStructName(structName),
	})

}

func readSetConfigurerForStruct(structName string) string {

	return templateOutput(readSetConfigurerTemplate, map[string]string{
		"firstLetter": strings.ToLower(structName[:1]),
		"structName":  structName,
		"readerName":  readerStructName(structName),
	})

}
<commit_msg>Tweak the location of two structs and document what they do to facilitate further refactoring. Part of #746.<commit_after>package codegen

import (
	"errors"
	"go/build"
	"go/format"
	"log"
	"strings"
	"unicode"

	"github.com/MarcGrol/golangAnnotations/model"
	"github.com/MarcGrol/golangAnnotations/parser"
	"github.com/jkomoros/boardgame"
)

type memoizedEmbeddedStructKey struct {
	Import           string
	TargetStructName string
}

var memoizedEmbeddedStructs map[memoizedEmbeddedStructKey]*typeInfo

//typeInfo is a collection of information about the specific fields in a given
//struct, including their boardgame.PropertyType, whether they're the mutable
//version, and if they're actually a higher type in the hierarchy (e.g.
//MergedStack instead of just a STack).
type typeInfo struct {
	Types       map[string]boardgame.PropertyType
	Mutable     map[string]bool
	UpConverter map[string]string
}

//nameForTypeInfo represents each named struct field of a given
//boardgame.PropertyType that we'll output, including enough information to know
//if it needs mutable/upconverter.
type nameForTypeInfo struct {
	Name        string
	Mutable     bool
	UpConverter string
}

const magicDocLinePrefix = "boardgame:codegen"

func init() {
	memoizedEmbeddedStructs = make(map[memoizedEmbeddedStructKey]*typeInfo)
}

/*

ProcessReaders operates on the package at the given relative location, and
produces two strings, one that is appropriate to be saved in auto_reader.go,
and one that is appropriate to be saved in auto_reader_test.go.

ProcessReaders processes a package of go files, searching for structs that
have a comment immediately above their declaration that begins with
"boardgame:codegen". For each such struct, it creates a Reader(), ReadSetter(),
and ReadSetConfigurer() method that implement boardgame.Reader,
boardgame.ReadSetter, and boardgame.ReadSetConfigurer, respectively.

Producing a ReadSetConfigurator requires a ReadSetter, and producing a
ReadSetter requires a Reader. By default if you have the magic comment of
`boardgame:codegen` it with produce all three. However, if you want only some of
the methods, include an argument for the highest one you want, e.g.
`boardgame:codegen readsetter` to generate a Reader() and ReadSetter().

This package will automatically create additional type transform methods
to handle fields whose literal type is boardgame.ImmutableSizedStack,
boardgame.SizedStack, boardgame.MergedStack, enum.RangeValue, and
enum.TreeValue.

The outputted readers, readsetters, and readsetconfigurers use a hard-
coded list of fields for performance (reflection would be about 30% slower
under normal usage). You should re-generate output every time you add a
struct or modify the fields on a struct.

*/
func ProcessReaders(location string) (output string, testOutput string, err error) {

	sources, err := parser.ParseSourceDir(location, ".*")

	if err != nil {
		return "", "", errors.New("Couldn't parse sources: " + err.Error())
	}

	output, err = doProcessStructs(sources, location, false)

	if err != nil {
		return "", "", errors.New("Couldn't process non-test files: " + err.Error())
	}

	testOutput, err = doProcessStructs(sources, location, true)

	if err != nil {
		return "", "", errors.New("Couldn't process test files: " + err.Error())
	}

	formattedBytes, err := format.Source([]byte(output))

	if err != nil {
		if debugSaveBadCode {
			formattedBytes = []byte(output)
		} else {
			return "", "", errors.New("Couldn't go fmt code for reader: " + err.Error())
		}
	}

	formattedTestBytes, err := format.Source([]byte(testOutput))

	if err != nil {
		if debugSaveBadCode {
			formattedTestBytes = []byte(testOutput)
		} else {
			return "", "", errors.New("Couldn't go fmt code for test reader: " + err.Error())
		}
	}

	return string(formattedBytes), string(formattedTestBytes), nil
}

func doProcessStructs(sources model.ParsedSources, location string, testFiles bool) (output string, err error) {

	if len(sources.Structs) == 0 {
		//If there are no structs at all that's OK, just don't ouput anything.
		return "", nil
	}

	for _, theStruct := range sources.Structs {

		//Only process structs in test or not test files, depending on which mode we're in.
		structInTestFile := strings.HasSuffix(theStruct.Filename, "_test.go")

		if structInTestFile != testFiles {
			continue
		}

		generator := newReaderGenerator(theStruct, location, sources.Structs)

		if generator == nil {
			//No utput necessary
			continue
		}

		output += generator.Output()
	}

	if output != "" {
		output = headerForPackage(sources.Structs[0].PackageName) + output
	}

	return output, nil
}

func fieldNamePublic(name string) bool {
	if len(name) < 1 {
		return false
	}

	firstChar := []rune(name)[0]

	if firstChar != unicode.ToUpper(firstChar) {
		//It was not upper case, thus private, thus should not be included.
		return false
	}

	//TODO: check if the struct says propertyreader:omit

	return true
}

//fieldNamePossibleEmbeddedStruct returns true if it's possible that the field
//is an embedded struct.
func fieldNamePossibleEmbeddedStruct(theField model.Field) bool {

	theTypeParts := strings.Split(theField.TypeName, ".")

	if len(theTypeParts) != 2 {
		return false
	}

	if theField.Name == theTypeParts[0] {
		return true
	}

	return false
}

func structTypes(location string, theStruct model.Struct, allStructs []model.Struct) *typeInfo {

	result := &typeInfo{
		make(map[string]boardgame.PropertyType),
		make(map[string]bool),
		make(map[string]string),
	}

	for _, field := range theStruct.Fields {
		if fieldNamePossibleEmbeddedStruct(field) {
			embeddedInfo := typesForPossibleEmbeddedStruct(location, field, allStructs)
			if embeddedInfo != nil {
				for key, val := range embeddedInfo.Types {
					result.Types[key] = val
				}
				for key, val := range embeddedInfo.Mutable {
					result.Mutable[key] = val
				}
				continue
			}
		}
		//Check if it's a local-to-package anonymous embedded struct
		if field.Name == "" {
			foundStruct := false
			for _, otherStruct := range allStructs {
				if otherStruct.Name == field.TypeName {
					embeddedInfo := structTypes(location, otherStruct, allStructs)
					for key, val := range embeddedInfo.Types {
						result.Types[key] = val
					}
					for key, val := range embeddedInfo.Mutable {
						result.Mutable[key] = val
					}
					foundStruct = true
					break
				}
			}
			if foundStruct {
				continue
			}
		}
		if !fieldNamePublic(field.Name) {
			continue
		}
		switch field.TypeName {
		case "int":
			if field.IsSlice {
				result.Types[field.Name] = boardgame.TypeIntSlice
			} else {
				result.Types[field.Name] = boardgame.TypeInt
			}
			result.Mutable[field.Name] = true
		case "bool":
			if field.IsSlice {
				result.Types[field.Name] = boardgame.TypeBoolSlice
			} else {
				result.Types[field.Name] = boardgame.TypeBool
			}
			result.Mutable[field.Name] = true
		case "string":
			if field.IsSlice {
				result.Types[field.Name] = boardgame.TypeStringSlice
			} else {
				result.Types[field.Name] = boardgame.TypeString
			}
			result.Mutable[field.Name] = true
		case "boardgame.PlayerIndex":
			if field.IsSlice {
				result.Types[field.Name] = boardgame.TypePlayerIndexSlice
			} else {
				result.Types[field.Name] = boardgame.TypePlayerIndex
			}
			result.Mutable[field.Name] = true
		case "boardgame.ImmutableStack":
			result.Types[field.Name] = boardgame.TypeStack
			result.Mutable[field.Name] = false
		case "boardgame.MergedStack":
			result.Types[field.Name] = boardgame.TypeStack
			result.Mutable[field.Name] = false
			result.UpConverter[field.Name] = "MergedStack"
		case "boardgame.Stack":
			result.Types[field.Name] = boardgame.TypeStack
			result.Mutable[field.Name] = true
		case "boardgame.ImmutableSizedStack":
			result.Types[field.Name] = boardgame.TypeStack
			result.Mutable[field.Name] = false
			result.UpConverter[field.Name] = "ImmutableSizedStack"
		case "boardgame.SizedStack":
			result.Types[field.Name] = boardgame.TypeStack
			result.Mutable[field.Name] = true
			result.UpConverter[field.Name] = "SizedStack"
		case "boardgame.ImmutableBoard":
			result.Types[field.Name] = boardgame.TypeBoard
			result.Mutable[field.Name] = false
		case "boardgame.Board":
			result.Types[field.Name] = boardgame.TypeBoard
			result.Mutable[field.Name] = true
		case "enum.ImmutableVal":
			result.Types[field.Name] = boardgame.TypeEnum
			result.Mutable[field.Name] = false
		case "enum.Val":
			result.Types[field.Name] = boardgame.TypeEnum
			result.Mutable[field.Name] = true
		case "enum.ImmutableRangeVal":
			result.Types[field.Name] = boardgame.TypeEnum
			result.Mutable[field.Name] = false
			result.UpConverter[field.Name] = "ImmutableRangeVal"
		case "enum.RangeVal":
			result.Types[field.Name] = boardgame.TypeEnum
			result.Mutable[field.Name] = true
			result.UpConverter[field.Name] = "RangeVal"
		case "enum.ImmutableTreeVal":
			result.Types[field.Name] = boardgame.TypeEnum
			result.Mutable[field.Name] = false
			result.UpConverter[field.Name] = "ImmutableTreeVal"
		case "enum.TreeVal":
			result.Types[field.Name] = boardgame.TypeEnum
			result.Mutable[field.Name] = true
			result.UpConverter[field.Name] = "TreeVal"
		case "boardgame.ImmutableTimer":
			result.Types[field.Name] = boardgame.TypeTimer
			result.Mutable[field.Name] = false
		case "boardgame.Timer":
			result.Types[field.Name] = boardgame.TypeTimer
			result.Mutable[field.Name] = true
		default:
			log.Println("Unknown type on " + theStruct.Name + ": " + field.Name + ": " + field.TypeName)
		}
	}

	return result
}

//typeforPossibleEmbeddedStruct should be called when we think that an unknown
//field MIGHT be an embedded struct. If it is, we will identify the package it
//appears to be built from, parse those structs, try to find the struct, and
//return a map of property types in it.
func typesForPossibleEmbeddedStruct(location string, theField model.Field, allStructs []model.Struct) *typeInfo {

	targetTypeParts := strings.Split(theField.TypeName, ".")

	if len(targetTypeParts) != 2 {
		return nil
	}

	targetType := targetTypeParts[1]

	//base.SubState will be anonymously embedded but should be ignored.
	if targetTypeParts[0] == "base" && targetType == "SubState" {
		return nil
	}

	key := memoizedEmbeddedStructKey{
		Import:           theField.PackageName,
		TargetStructName: targetType,
	}

	foundTypes := memoizedEmbeddedStructs[key]

	if foundTypes != nil {
		return foundTypes
	}

	pkg, err := build.Import(theField.PackageName, location, build.FindOnly)

	if err != nil {
		log.Println("Couldn't find canonical import: " + err.Error())
		return nil
	}

	importPath := pkg.Dir

	sources, err := parser.ParseSourceDir(importPath, ".*")

	if err != nil {
		log.Println("Error in sources for ", theField, err.Error())
		return nil
	}

	for _, theStruct := range sources.Structs {
		if theStruct.Name != targetType {
			continue
		}
		//Found it!
		foundTypes = structTypes(location, theStruct, allStructs)

		memoizedEmbeddedStructs[key] = foundTypes

	}

	return foundTypes

}

func structConfig(docLines []string) (outputReader bool, outputReadSetter bool, outputReadSetConfigurer bool) {

	for _, mainDocLine := range docLines {
		//Multi-line comments will come in as one docline.
		for _, docLine := range strings.Split(mainDocLine, "\n") {
			docLine = strings.ToLower(docLine)
			docLine = strings.TrimPrefix(docLine, "//")
			docLine = strings.TrimSpace(docLine)
			if !strings.HasPrefix(docLine, magicDocLinePrefix) {
				continue
			}
			docLine = strings.TrimPrefix(docLine, magicDocLinePrefix)
			docLine = strings.TrimSpace(docLine)

			switch docLine {
			case "", "all", "readsetconfigurer":
				return true, true, true
			case "readsetter":
				return true, true, false
			case "reader":
				return true, false, false
			}
		}
	}
	return false, false, false
}

func headerForPackage(packageName string) string {

	return templateOutput(headerTemplate, map[string]string{
		"packageName": packageName,
	}) + importText
}

//readerStructName returns the name of the auto-generated reader struct for the
//given struct.
func readerStructName(structName string) string {
	//The prefix used to be "__" but that didn't lint correctly, so instead use
	//a non-latin prefix character that is like an a but with a dot (to make it
	//less likely to show up in autocompletes in IDEs)
	return "ȧutoGenerated" + strings.Title(structName) + "Reader"
}

func readerForStruct(structName string) string {

	return templateOutput(readerTemplate, map[string]string{
		"firstLetter": strings.ToLower(structName[:1]),
		"structName":  structName,
		"readerName":  readerStructName(structName),
	})

}

func readSetterForStruct(structName string) string {

	return templateOutput(readSetterTemplate, map[string]string{
		"firstLetter": strings.ToLower(structName[:1]),
		"structName":  structName,
		"readerName":  readerStructName(structName),
	})

}

func readSetConfigurerForStruct(structName string) string {

	return templateOutput(readSetConfigurerTemplate, map[string]string{
		"firstLetter": strings.ToLower(structName[:1]),
		"structName":  structName,
		"readerName":  readerStructName(structName),
	})

}
<|endoftext|>