<commit_before>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 vpp

import (
	"strings"

	"github.com/ligato/cn-infra/datasync"
	linux_ifaceidx "github.com/ligato/vpp-agent/plugins/linux/ifplugin/ifaceidx"
	"github.com/ligato/vpp-agent/plugins/vpp/ifplugin/ifaceidx"
	"github.com/ligato/vpp-agent/plugins/vpp/l2plugin/l2idx"
	"github.com/ligato/vpp-agent/plugins/vpp/model/interfaces"
	"github.com/ligato/vpp-agent/plugins/vpp/model/l2"
	"golang.org/x/net/context"
)

// WatchEvents goroutine is used to watch for changes in the northbound configuration & NameToIdxMapping notifications.
func (plugin *Plugin) watchEvents(ctx context.Context) {
	plugin.wg.Add(1)
	defer plugin.wg.Done()

	runWithMutex := func(fn func()) {
		if plugin.WatchEventsMutex != nil {
			plugin.WatchEventsMutex.Lock()
			defer plugin.WatchEventsMutex.Unlock()
		}
		fn()
	}

	for {
		select {
		case e := <-plugin.resyncConfigChan:
			runWithMutex(func() {
				plugin.onResyncEvent(e)
			})

		case e := <-plugin.resyncStatusChan:
			runWithMutex(func() {
				plugin.onStatusResyncEvent(e)
			})

		case e := <-plugin.changeChan:
			runWithMutex(func() {
				plugin.onChangeEvent(e)
			})

		case e := <-plugin.ifIdxWatchCh:
			runWithMutex(func() {
				plugin.onVppIfaceEvent(e)
			})

		case e := <-plugin.linuxIfIdxWatchCh:
			runWithMutex(func() {
				plugin.onLinuxIfaceEvent(e)
			})

		case e := <-plugin.bdIdxWatchCh:
			runWithMutex(func() {
				plugin.onVppBdEvent(e)
			})

		case <-ctx.Done():
			plugin.Log.Debug("Stop watching events")
			return
		}
	}
}

func (plugin *Plugin) onResyncEvent(e datasync.ResyncEvent) {
	req := plugin.resyncParseEvent(e)
	var err error
	if plugin.resyncStrategy == skipResync {
		// skip resync
		plugin.Log.Info("skip VPP resync strategy chosen, VPP resync is omitted")
	} else if plugin.resyncStrategy == optimizeColdStart {
		// optimize resync
		err = plugin.resyncConfigPropageOptimizedRequest(req)
	} else {
		// full resync
		err = plugin.resyncConfigPropageFullRequest(req)
	}
	e.Done(err)
}

func (plugin *Plugin) onStatusResyncEvent(e datasync.ResyncEvent) {
	var wasError error
	for key, vals := range e.GetValues() {
		plugin.Log.Debugf("trying to delete obsolete status for key %v begin ", key)
		if strings.HasPrefix(key, interfaces.StatePrefix) {
			var keys []string
			for {
				x, stop := vals.GetNext()
				if stop {
					break
				}
				keys = append(keys, x.GetKey())
			}
			if len(keys) > 0 {
				err := plugin.resyncIfStateEvents(keys)
				if err != nil {
					wasError = err
				}
			}
		} else if strings.HasPrefix(key, l2.BdStatePrefix) {
			var keys []string
			for {
				x, stop := vals.GetNext()
				if stop {
					break
				}
				keys = append(keys, x.GetKey())
			}
			if len(keys) > 0 {
				err := plugin.resyncBdStateEvents(keys)
				if err != nil {
					wasError = err
				}
			}
		}
	}
	e.Done(wasError)
}

func (plugin *Plugin) onChangeEvent(e datasync.ChangeEvent) {
	// For asynchronous calls only: if changePropagateRequest ends up without errors,
	// the dataChng.Done is called in particular vppcall, otherwise the dataChng.Done is called here.
	callbackCalled, err := plugin.changePropagateRequest(e, e.Done)
	// When the request propagation is complete, send the error context (even if the error is nil).
	plugin.errorChannel <- ErrCtx{e, err}
	if !callbackCalled {
		e.Done(err)
	}
}

func (plugin *Plugin) onVppIfaceEvent(e ifaceidx.SwIfIdxDto) {
	if !e.IsDelete() {
		// Keep order.
		if err := plugin.aclConfigurator.ResolveCreatedInterface(e.Name, e.Idx); err != nil {
			plugin.aclConfigurator.LogError(err)
		}
		if err := plugin.arpConfigurator.ResolveCreatedInterface(e.Name); err != nil {
			plugin.arpConfigurator.LogError(err)
		}
		if err := plugin.proxyArpConfigurator.ResolveCreatedInterface(e.Name, e.Idx); err != nil {
			plugin.proxyArpConfigurator.LogError(err)
		}
		if err := plugin.bdConfigurator.ResolveCreatedInterface(e.Name, e.Idx); err != nil {
			plugin.bdConfigurator.LogError(err)
		}
		plugin.fibConfigurator.ResolveCreatedInterface(e.Name, e.Idx, func(err error) {
			if err != nil {
				plugin.fibConfigurator.LogError(err)
			}
		})
		if err := plugin.xcConfigurator.ResolveCreatedInterface(e.Name); err != nil {
			plugin.xcConfigurator.LogError(err)
		}
		if err := plugin.appNsConfigurator.ResolveCreatedInterface(e.Name, e.Idx); err != nil {
			plugin.appNsConfigurator.LogError(err)
		}
		if err := plugin.stnConfigurator.ResolveCreatedInterface(e.Name); err != nil {
			plugin.stnConfigurator.LogError(err)
		}
		if err := plugin.routeConfigurator.ResolveCreatedInterface(e.Name, e.Idx); err != nil {
			plugin.routeConfigurator.LogError(err)
		}
		if err := plugin.natConfigurator.ResolveCreatedInterface(e.Name, e.Idx); err != nil {
			plugin.natConfigurator.LogError(err)
		}
		if err := plugin.ipSecConfigurator.ResolveCreatedInterface(e.Name, e.Idx); err != nil {
			plugin.ipSecConfigurator.LogError(err)
		}
		// TODO propagate error
	} else {
		if err := plugin.aclConfigurator.ResolveDeletedInterface(e.Name, e.Idx); err != nil {
			plugin.aclConfigurator.LogError(err)
		}
		if err := plugin.arpConfigurator.ResolveDeletedInterface(e.Name, e.Idx); err != nil {
			plugin.arpConfigurator.LogError(err)
		}
		if err := plugin.proxyArpConfigurator.ResolveDeletedInterface(e.Name); err != nil {
			plugin.proxyArpConfigurator.LogError(err)
		}
		if err := plugin.bdConfigurator.ResolveDeletedInterface(e.Name); err != nil {
			plugin.bdConfigurator.LogError(err)
		}
		plugin.fibConfigurator.ResolveDeletedInterface(e.Name, e.Idx, func(err error) {
			if err != nil {
				plugin.fibConfigurator.LogError(err)
			}
		})
		if err := plugin.xcConfigurator.ResolveDeletedInterface(e.Name); err != nil {
			plugin.xcConfigurator.LogError(err)
		}
		if err := plugin.appNsConfigurator.ResolveDeletedInterface(e.Name, e.Idx); err != nil {
			plugin.appNsConfigurator.LogError(err)
		}
		if err := plugin.stnConfigurator.ResolveDeletedInterface(e.Name); err != nil {
			plugin.stnConfigurator.LogError(err)
		}
		if err := plugin.routeConfigurator.ResolveDeletedInterface(e.Name, e.Idx); err != nil {
			plugin.routeConfigurator.LogError(err)
		}
		if err := plugin.natConfigurator.ResolveDeletedInterface(e.Name, e.Idx); err != nil {
			plugin.natConfigurator.LogError(err)
		}
		if err := plugin.ipSecConfigurator.ResolveDeletedInterface(e.Name, e.Idx); err != nil {
			plugin.ipSecConfigurator.LogError(err)
		}
		// TODO propagate error
	}
	e.Done()
}

func (plugin *Plugin) onLinuxIfaceEvent(e linux_ifaceidx.LinuxIfIndexDto) {
	var hostIfName string
	if e.Metadata != nil && e.Metadata.Data != nil && e.Metadata.Data.HostIfName != "" {
		hostIfName = e.Metadata.Data.HostIfName
	}
	var err error
	if !e.IsDelete() {
		err = plugin.ifConfigurator.ResolveCreatedLinuxInterface(e.Name, hostIfName, e.Idx)
	} else {
		err = plugin.ifConfigurator.ResolveDeletedLinuxInterface(e.Name, hostIfName, e.Idx)
	}
	plugin.ifConfigurator.LogError(err)
	e.Done()
}

func (plugin *Plugin) onVppBdEvent(e l2idx.BdChangeDto) {
	if e.IsDelete() {
		plugin.fibConfigurator.ResolveDeletedBridgeDomain(e.Name, e.Idx, func(err error) {
			if err != nil {
				plugin.fibConfigurator.LogError(err)
			}
		})
		// TODO propagate error
	} else if e.IsUpdate() {
		plugin.fibConfigurator.ResolveUpdatedBridgeDomain(e.Name, e.Idx, func(err error) {
			if err != nil {
				plugin.fibConfigurator.LogError(err)
			}
		})
		// TODO propagate error
	} else {
		plugin.fibConfigurator.ResolveCreatedBridgeDomain(e.Name, e.Idx, func(err error) {
			if err != nil {
				plugin.fibConfigurator.LogError(err)
			}
		})
		// TODO propagate error
	}
	e.Done()
}
<commit_msg>do not handle interface mapping update as create<commit_after>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 vpp

import (
	"strings"

	"github.com/ligato/cn-infra/datasync"
	linux_ifaceidx "github.com/ligato/vpp-agent/plugins/linux/ifplugin/ifaceidx"
	"github.com/ligato/vpp-agent/plugins/vpp/ifplugin/ifaceidx"
	"github.com/ligato/vpp-agent/plugins/vpp/l2plugin/l2idx"
	"github.com/ligato/vpp-agent/plugins/vpp/model/interfaces"
	"github.com/ligato/vpp-agent/plugins/vpp/model/l2"
	"golang.org/x/net/context"
)

// WatchEvents goroutine is used to watch for changes in the northbound configuration & NameToIdxMapping notifications.
func (plugin *Plugin) watchEvents(ctx context.Context) {
	plugin.wg.Add(1)
	defer plugin.wg.Done()

	runWithMutex := func(fn func()) {
		if plugin.WatchEventsMutex != nil {
			plugin.WatchEventsMutex.Lock()
			defer plugin.WatchEventsMutex.Unlock()
		}
		fn()
	}

	for {
		select {
		case e := <-plugin.resyncConfigChan:
			runWithMutex(func() {
				plugin.onResyncEvent(e)
			})

		case e := <-plugin.resyncStatusChan:
			runWithMutex(func() {
				plugin.onStatusResyncEvent(e)
			})

		case e := <-plugin.changeChan:
			runWithMutex(func() {
				plugin.onChangeEvent(e)
			})

		case e := <-plugin.ifIdxWatchCh:
			runWithMutex(func() {
				plugin.onVppIfaceEvent(e)
			})

		case e := <-plugin.linuxIfIdxWatchCh:
			runWithMutex(func() {
				plugin.onLinuxIfaceEvent(e)
			})

		case e := <-plugin.bdIdxWatchCh:
			runWithMutex(func() {
				plugin.onVppBdEvent(e)
			})

		case <-ctx.Done():
			plugin.Log.Debug("Stop watching events")
			return
		}
	}
}

func (plugin *Plugin) onResyncEvent(e datasync.ResyncEvent) {
	req := plugin.resyncParseEvent(e)
	var err error
	if plugin.resyncStrategy == skipResync {
		// skip resync
		plugin.Log.Info("skip VPP resync strategy chosen, VPP resync is omitted")
	} else if plugin.resyncStrategy == optimizeColdStart {
		// optimize resync
		err = plugin.resyncConfigPropageOptimizedRequest(req)
	} else {
		// full resync
		err = plugin.resyncConfigPropageFullRequest(req)
	}
	e.Done(err)
}

func (plugin *Plugin) onStatusResyncEvent(e datasync.ResyncEvent) {
	var wasError error
	for key, vals := range e.GetValues() {
		plugin.Log.Debugf("trying to delete obsolete status for key %v begin ", key)
		if strings.HasPrefix(key, interfaces.StatePrefix) {
			var keys []string
			for {
				x, stop := vals.GetNext()
				if stop {
					break
				}
				keys = append(keys, x.GetKey())
			}
			if len(keys) > 0 {
				err := plugin.resyncIfStateEvents(keys)
				if err != nil {
					wasError = err
				}
			}
		} else if strings.HasPrefix(key, l2.BdStatePrefix) {
			var keys []string
			for {
				x, stop := vals.GetNext()
				if stop {
					break
				}
				keys = append(keys, x.GetKey())
			}
			if len(keys) > 0 {
				err := plugin.resyncBdStateEvents(keys)
				if err != nil {
					wasError = err
				}
			}
		}
	}
	e.Done(wasError)
}

func (plugin *Plugin) onChangeEvent(e datasync.ChangeEvent) {
	// For asynchronous calls only: if changePropagateRequest ends up without errors,
	// the dataChng.Done is called in particular vppcall, otherwise the dataChng.Done is called here.
	callbackCalled, err := plugin.changePropagateRequest(e, e.Done)
	// When the request propagation is complete, send the error context (even if the error is nil).
	plugin.errorChannel <- ErrCtx{e, err}
	if !callbackCalled {
		e.Done(err)
	}
}

func (plugin *Plugin) onVppIfaceEvent(e ifaceidx.SwIfIdxDto) {
	if e.IsDelete() {
		if err := plugin.aclConfigurator.ResolveDeletedInterface(e.Name, e.Idx); err != nil {
			plugin.aclConfigurator.LogError(err)
		}
		if err := plugin.arpConfigurator.ResolveDeletedInterface(e.Name, e.Idx); err != nil {
			plugin.arpConfigurator.LogError(err)
		}
		if err := plugin.proxyArpConfigurator.ResolveDeletedInterface(e.Name); err != nil {
			plugin.proxyArpConfigurator.LogError(err)
		}
		if err := plugin.bdConfigurator.ResolveDeletedInterface(e.Name); err != nil {
			plugin.bdConfigurator.LogError(err)
		}
		plugin.fibConfigurator.ResolveDeletedInterface(e.Name, e.Idx, func(err error) {
			if err != nil {
				plugin.fibConfigurator.LogError(err)
			}
		})
		if err := plugin.xcConfigurator.ResolveDeletedInterface(e.Name); err != nil {
			plugin.xcConfigurator.LogError(err)
		}
		if err := plugin.appNsConfigurator.ResolveDeletedInterface(e.Name, e.Idx); err != nil {
			plugin.appNsConfigurator.LogError(err)
		}
		if err := plugin.stnConfigurator.ResolveDeletedInterface(e.Name); err != nil {
			plugin.stnConfigurator.LogError(err)
		}
		if err := plugin.routeConfigurator.ResolveDeletedInterface(e.Name, e.Idx); err != nil {
			plugin.routeConfigurator.LogError(err)
		}
		if err := plugin.natConfigurator.ResolveDeletedInterface(e.Name, e.Idx); err != nil {
			plugin.natConfigurator.LogError(err)
		}
		if err := plugin.ipSecConfigurator.ResolveDeletedInterface(e.Name, e.Idx); err != nil {
			plugin.ipSecConfigurator.LogError(err)
		}
		// TODO propagate error
	} else if e.IsUpdate() {
		// Nothing to do here
	} else {
		// Keep order.
		if err := plugin.aclConfigurator.ResolveCreatedInterface(e.Name, e.Idx); err != nil {
			plugin.aclConfigurator.LogError(err)
		}
		if err := plugin.arpConfigurator.ResolveCreatedInterface(e.Name); err != nil {
			plugin.arpConfigurator.LogError(err)
		}
		if err := plugin.proxyArpConfigurator.ResolveCreatedInterface(e.Name, e.Idx); err != nil {
			plugin.proxyArpConfigurator.LogError(err)
		}
		if err := plugin.bdConfigurator.ResolveCreatedInterface(e.Name, e.Idx); err != nil {
			plugin.bdConfigurator.LogError(err)
		}
		plugin.fibConfigurator.ResolveCreatedInterface(e.Name, e.Idx, func(err error) {
			if err != nil {
				plugin.fibConfigurator.LogError(err)
			}
		})
		if err := plugin.xcConfigurator.ResolveCreatedInterface(e.Name); err != nil {
			plugin.xcConfigurator.LogError(err)
		}
		if err := plugin.appNsConfigurator.ResolveCreatedInterface(e.Name, e.Idx); err != nil {
			plugin.appNsConfigurator.LogError(err)
		}
		if err := plugin.stnConfigurator.ResolveCreatedInterface(e.Name); err != nil {
			plugin.stnConfigurator.LogError(err)
		}
		if err := plugin.routeConfigurator.ResolveCreatedInterface(e.Name, e.Idx); err != nil {
			plugin.routeConfigurator.LogError(err)
		}
		if err := plugin.natConfigurator.ResolveCreatedInterface(e.Name, e.Idx); err != nil {
			plugin.natConfigurator.LogError(err)
		}
		if err := plugin.ipSecConfigurator.ResolveCreatedInterface(e.Name, e.Idx); err != nil {
			plugin.ipSecConfigurator.LogError(err)
		}
		// TODO propagate error
	}
	e.Done()
}

func (plugin *Plugin) onLinuxIfaceEvent(e linux_ifaceidx.LinuxIfIndexDto) {
	var hostIfName string
	if e.Metadata != nil && e.Metadata.Data != nil && e.Metadata.Data.HostIfName != "" {
		hostIfName = e.Metadata.Data.HostIfName
	}
	var err error
	if !e.IsDelete() {
		err = plugin.ifConfigurator.ResolveCreatedLinuxInterface(e.Name, hostIfName, e.Idx)
	} else {
		err = plugin.ifConfigurator.ResolveDeletedLinuxInterface(e.Name, hostIfName, e.Idx)
	}
	plugin.ifConfigurator.LogError(err)
	e.Done()
}

func (plugin *Plugin) onVppBdEvent(e l2idx.BdChangeDto) {
	if e.IsDelete() {
		plugin.fibConfigurator.ResolveDeletedBridgeDomain(e.Name, e.Idx, func(err error) {
			if err != nil {
				plugin.fibConfigurator.LogError(err)
			}
		})
		// TODO propagate error
	} else if e.IsUpdate() {
		plugin.fibConfigurator.ResolveUpdatedBridgeDomain(e.Name, e.Idx, func(err error) {
			if err != nil {
				plugin.fibConfigurator.LogError(err)
			}
		})
		// TODO propagate error
	} else {
		plugin.fibConfigurator.ResolveCreatedBridgeDomain(e.Name, e.Idx, func(err error) {
			if err != nil {
				plugin.fibConfigurator.LogError(err)
			}
		})
		// TODO propagate error
	}
	e.Done()
}
<|endoftext|>


<commit_before>// Copyright (c) 2013 Mathieu Turcotte
// Licensed under the MIT license.

package browserchannel

import (
	"net/url"
	"reflect"
	"testing"
)

// TODO: Add cases with port number.
func TestOriginMatcher(t *testing.T) {
	cases := []struct {
		origin   string
		expected bool
	}{
		{"http://1.bc.duplika.ca", true},
		{"https://bc.duplika.ca", true},
		{"https://duplika.ca", true},
		{"http://duplika.ca", true},
		{"http://plika.ca", false},
		{"http://.duplika.ca", false},
		{"http://duplika", false},
		{"duplika.ca", false},
	}

	matcher := makeOriginMatcher("duplika.ca")

	for _, c := range cases {
		m := matcher.MatchString(c.origin)
		if m != c.expected {
			t.Errorf("expected %v, got %v for %s", c.expected, m, c.origin)
		}
	}
}

func TestParseIncomingMaps(t *testing.T) {
	cases := []struct {
		qs     string
		offset int
		maps   []Map
		err    error
	}{
		// Empty request body.
		{"", 0, []Map{}, nil},
		// Request body with a count of 0 maps.
		{"count=0", 0, []Map{}, nil},
		// Request body with a map.
		{"count=1&ofs=0&req0_timestamp=1364151246289&req0_id=0",
			0, []Map{{"timestamp": "1364151246289", "id": "0"}}, nil},
		// Request body with two maps.
		{"count=2&ofs=10&req0_key1=foo&req1_key2=bar",
			10, []Map{{"key1": "foo"}, {"key2": "bar"}}, nil},
		// Request body with invalid request id (req2 should be req1).
		{"count=2&ofs=10&req0_key=val&req3_key=val", 0, nil, ErrBadMap},
		// Request body with an invalid offset value.
		{"count=1&ofs=abc&req0_key=val", 0, nil, ErrBadMap},
		// Request body with an invalid key id.
		{"count=1&ofs=abc&reqABC_key=val", 0, nil, ErrBadMap},
	}

	for i, c := range cases {
		values, _ := url.ParseQuery(c.qs)
		offset, maps, err := parseIncomingMaps(values)
		if err != c.err {
			t.Errorf("case %d: expected error %v, got %s", i, c.err, err)
		}
		if offset != c.offset {
			t.Errorf("case %d: expected offset %v, got %v", i, c.offset, offset)
		}
		if !reflect.DeepEqual(maps, c.maps) {
			t.Errorf("case %d: expected maps %#v, got %#v", i, c.maps, maps)
		}
	}
}
<commit_msg>Fix utils tests.<commit_after>// Copyright (c) 2013 Mathieu Turcotte
// Licensed under the MIT license.

package browserchannel

import (
	"net/url"
	"reflect"
	"testing"
)

// TODO: Add cases with port number.
func TestOriginMatcher(t *testing.T) {
	cases := []struct {
		origin   string
		expected bool
	}{
		{"http://1.bc.duplika.ca", true},
		{"https://bc.duplika.ca", true},
		{"https://duplika.ca", true},
		{"http://duplika.ca", true},
		{"http://plika.ca", false},
		{"http://.duplika.ca", false},
		{"http://duplika", false},
		{"duplika.ca", false},
	}

	matcher := makeOriginMatcher("duplika.ca")

	for _, c := range cases {
		m := matcher.MatchString(c.origin)
		if m != c.expected {
			t.Errorf("expected %v, got %v for %s", c.expected, m, c.origin)
		}
	}
}

func TestParseIncomingMaps(t *testing.T) {
	cases := []struct {
		qs     string
		offset int
		maps   []Map
		err    error
	}{
		// Empty request body.
		{"", 0, []Map{}, nil},
		// Request body with a count of 0 maps.
		{"count=0", 0, []Map{}, nil},
		// Request body with a map.
		{"count=1&ofs=0&req0_timestamp=1364151246289&req0_id=0",
			0, []Map{{"timestamp": "1364151246289", "id": "0"}}, nil},
		// Request body with two maps.
		{"count=2&ofs=10&req0_key1=foo&req1_key2=bar",
			10, []Map{{"key1": "foo"}, {"key2": "bar"}}, nil},
		// Request body with invalid request id (req2 should be req1).
		{"count=2&ofs=10&req0_key=val&req3_key=val", 0, nil, errBadMap},
		// Request body with an invalid offset value.
		{"count=1&ofs=abc&req0_key=val", 0, nil, errBadMap},
		// Request body with an invalid key id.
		{"count=1&ofs=abc&reqABC_key=val", 0, nil, errBadMap},
	}

	for i, c := range cases {
		values, _ := url.ParseQuery(c.qs)
		offset, maps, err := parseIncomingMaps(values)
		if err != c.err {
			t.Errorf("case %d: expected error %v, got %s", i, c.err, err)
		}
		if offset != c.offset {
			t.Errorf("case %d: expected offset %v, got %v", i, c.offset, offset)
		}
		if !reflect.DeepEqual(maps, c.maps) {
			t.Errorf("case %d: expected maps %#v, got %#v", i, c.maps, maps)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2017-2019 Authors of Cilium
//
// 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 cmd

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/cilium/cilium/pkg/components"
	"github.com/cilium/cilium/pkg/defaults"
)

// BugtoolConfiguration creates and loads the configuration file used to run
// the commands. The only commands not managed by the configuration is initial
// setup, for ex. searching for Cilium daemonset pods or running uname.
type BugtoolConfiguration struct {
	// Commands is the exact commands that will be run by the bugtool
	Commands []string `json:"commands"`
}

func setupDefaultConfig(path string, k8sPods []string, confDir, cmdDir string) (*BugtoolConfiguration, error) {
	c := BugtoolConfiguration{defaultCommands(confDir, cmdDir, k8sPods)}
	return &c, save(&c, path)
}

func defaultCommands(confDir string, cmdDir string, k8sPods []string) []string {
	var commands []string
	// Not expecting all of the commands to be available
	commands = []string{
		// Host and misc
		"ps auxfw",
		"hostname",
		"ip a",
		"ip -4 r",
		"ip -6 r",
		"ip -d -s l",
		"ip -4 n",
		"ip -6 n",
		"ss -t -p -a -i -s",
		"ss -u -p -a -i -s",
		"tc qdisc show",
		"tc -d -s qdisc show",
		"uname -a",
		"top -b -n 1",
		"uptime",
		"dmesg --time-format=iso",
		"sysctl -a",
		"bpftool map show",
		"bpftool prog show",
		// LB and CT map for debugging services; using bpftool for a reliable dump
		"bpftool map dump pinned /sys/fs/bpf/tc/globals/cilium_lb4_services_v2",
		"bpftool map dump pinned /sys/fs/bpf/tc/globals/cilium_lb4_services",
		"bpftool map dump pinned /sys/fs/bpf/tc/globals/cilium_lb4_backends",
		"bpftool map dump pinned /sys/fs/bpf/tc/globals/cilium_lb4_reverse_nat",
		"bpftool map dump pinned /sys/fs/bpf/tc/globals/cilium_ct4_global",
		"bpftool map dump pinned /sys/fs/bpf/tc/globals/cilium_ct_any4_global",
		"bpftool map dump pinned /sys/fs/bpf/tc/globals/cilium_lb4_affinity",
		"bpftool map dump pinned /sys/fs/bpf/tc/globals/cilium_lb6_affinity",
		"bpftool map dump pinned /sys/fs/bpf/tc/globals/cilium_lb_affinity_match",
		"bpftool map dump pinned /sys/fs/bpf/tc/globals/cilium_lb6_services_v2",
		"bpftool map dump pinned /sys/fs/bpf/tc/globals/cilium_lb6_services",
		"bpftool map dump pinned /sys/fs/bpf/tc/globals/cilium_lb6_backends",
		"bpftool map dump pinned /sys/fs/bpf/tc/globals/cilium_lb6_reverse_nat",
		"bpftool map dump pinned /sys/fs/bpf/tc/globals/cilium_ct6_global",
		"bpftool map dump pinned /sys/fs/bpf/tc/globals/cilium_ct_any6_global",
		"bpftool map dump pinned /sys/fs/bpf/tc/globals/cilium_snat_v4_external",
		"bpftool map dump pinned /sys/fs/bpf/tc/globals/cilium_snat_v6_external",
		// iptables
		"iptables-save -c",
		"iptables -S",
		"ip6tables -S",
		"iptables -L -v",
		"ip rule",
		"ip -4 route show table 2005",
		"ip -6 route show table 2005",
		"ip -4 route show table 200",
		"ip -6 route show table 200",
		// xfrm
		"ip xfrm policy",
		"ip -s xfrm state | awk '!/auth|enc|aead|auth-trunc|comp/'",
		// gops
		fmt.Sprintf("gops memstats $(pidof %s)", components.CiliumAgentName),
		fmt.Sprintf("gops stack $(pidof %s)", components.CiliumAgentName),
		fmt.Sprintf("gops stats $(pidof %s)", components.CiliumAgentName),
		// Get list of open file descriptors managed by the agent
		fmt.Sprintf("ls -la /proc/$(pidof %s)/fd", components.CiliumAgentName),
	}

	// Commands that require variables and / or more configuration are added
	// separately below
	commands = append(commands, catCommands()...)
	commands = append(commands, ethoolCommands()...)
	commands = append(commands, copyConfigCommands(confDir, k8sPods)...)
	commands = append(commands, copyCiliumInfoCommands(cmdDir, k8sPods)...)

	return k8sCommands(commands, k8sPods)
}

func save(c *BugtoolConfiguration, path string) error {
	f, err := os.Create(path)
	if err != nil {
		return fmt.Errorf("Failed to open file %s for writing: %s", path, err)
	}
	defer f.Close()

	data, err := json.MarshalIndent(c, "", "\t")
	if err != nil {
		return fmt.Errorf("Cannot marshal config %s", err)
	}
	err = ioutil.WriteFile(path, data, 0644)
	if err != nil {
		return fmt.Errorf("Cannot write config %s", err)
	}
	return nil
}

func loadConfigFile(path string) (*BugtoolConfiguration, error) {
	var content []byte
	var err error
	content, err = ioutil.ReadFile(path)

	if err != nil {
		return nil, err
	}

	var c BugtoolConfiguration
	err = json.Unmarshal(content, &c)
	return &c, err
}

func catCommands() []string {
	files := []string{
		"/proc/net/xfrm_stat",
		"/proc/sys/net/core/bpf_jit_enable",
		"/proc/kallsyms",
		"/etc/resolv.conf",
		"/var/log/docker.log",
		"/var/log/daemon.log",
		"/var/log/messages",
	}
	// Only print the files that do exist to reduce number of errors in
	// archive
	commands := []string{}
	for _, f := range files {
		if _, err := os.Stat(f); os.IsNotExist(err) {
			continue
		}
		commands = append(commands, fmt.Sprintf("cat %s", f))
	}
	// TODO: handle K8s case as well.
	return commands
}

func copyConfigCommands(confDir string, k8sPods []string) []string {
	commands := []string{}
	// Location is a convenience structure to avoid too many long lines
	type Location struct {
		Src string
		Dst string
	}

	// These locations don't depend on the kernel version for running so we
	// can add them in this scope.
	locations := []Location{
		{"/proc/config", fmt.Sprintf("%s/kernel-config", confDir)},
		{"/proc/config.gz", fmt.Sprintf("%s/kernel-config.gz", confDir)},
	}

	// The following lines copy the kernel configuration. This code is
	// duplicated for the non Kubernetes case. The variables preventing
	// them to be one block is the pod prefix and namespace used in the
	// path. This should be refactored.
	if len(k8sPods) == 0 {
		kernel, _ := execCommand("uname -r")
		kernel = strings.TrimSpace(kernel)
		// Append the boot config for the current kernel
		l := Location{fmt.Sprintf("/boot/config-%s", kernel),
			fmt.Sprintf("%s/kernel-config-%s", confDir, kernel)}
		locations = append(locations, l)

		// Use the locations to create command strings
		for _, location := range locations {
			if _, err := os.Stat(location.Src); os.IsNotExist(err) {
				continue
			}
			commands = append(commands, fmt.Sprintf("cp %s %s", location.Src, location.Dst))
		}
	} else {
		// If there are multiple pods, we want to get all of the kernel
		// configs. Therefore we need copy commands for all the pods.
		for _, pod := range k8sPods {
			prompt := podPrefix(pod, "uname -r")
			kernel, _ := execCommand(prompt)
			kernel = strings.TrimSpace(kernel)
			l := Location{fmt.Sprintf("/boot/config-%s", kernel),
				fmt.Sprintf("%s/kernel-config-%s", confDir, kernel)}
			locations = append(locations, l)

			// The location is mostly the same but the command is
			// prepended with 'kubectl` and the path contains the
			// namespace and pod. For ex:
			// kubectl cp kube-system/cilium-kg8lv:/tmp/cilium-bugtool-243785589.tar /tmp/cilium-bugtool-243785589.tar
			for _, location := range locations {
				kubectlArg := fmt.Sprintf("%s/%s:%s", k8sNamespace, pod, location.Src)
				cmd := fmt.Sprintf("%s %s %s %s", "kubectl", "cp", kubectlArg, location.Dst)
				commands = append(commands, cmd)
			}
		}
	}
	return commands
}

func copyCiliumInfoCommands(cmdDir string, k8sPods []string) []string {
	// Most of the output should come via debuginfo but also adding
	// these ones for skimming purposes
	ciliumCommands := []string{
		fmt.Sprintf("cilium debuginfo --output=markdown,json -f --output-directory=%s", cmdDir),
		"cilium metrics list",
		"cilium fqdn cache list",
		"cilium config",
		"cilium bpf bandwidth list",
		"cilium bpf tunnel list",
		"cilium bpf lb list",
		"cilium bpf endpoint list",
		"cilium bpf ct list global",
		"cilium bpf nat list",
		"cilium bpf ipmasq list",
		"cilium bpf ipcache list",
		"cilium bpf policy get --all --numeric",
		"cilium bpf sha list",
		"cilium bpf fs show",
		"cilium ip list -o json",
		"cilium map list --verbose",
		"cilium service list",
		"cilium status --verbose",
		"cilium identity list",
		"cilium-health status",
		"cilium policy selectors -o json",
		"cilium node list",
	}
	var commands []string

	stateDir := filepath.Join(defaults.RuntimePath, defaults.StateDir)
	if len(k8sPods) == 0 { // Assuming this is a non k8s deployment
		dst := filepath.Join(cmdDir, defaults.StateDir)
		commands = append(commands, fmt.Sprintf("cp -r %s %s", stateDir, dst))
		for _, cmd := range ciliumCommands {
			// Add the host flag if set
			if len(host) > 0 {
				cmd = fmt.Sprintf("%s -H %s", cmd, host)
			}
			commands = append(commands, cmd)
		}
	} else { // Found k8s pods
		for _, pod := range k8sPods {
			dst := filepath.Join(cmdDir, fmt.Sprintf("%s-%s", pod, defaults.StateDir))
			kubectlArg := fmt.Sprintf("%s/%s:%s", k8sNamespace, pod, stateDir)
			// kubectl cp kube-system/cilium-xrzwr:/var/run/cilium/state cilium-xrzwr-state
			commands = append(commands, fmt.Sprintf("kubectl cp %s %s", kubectlArg, dst))
			for _, cmd := range ciliumCommands {
				// Add the host flag if set
				if len(host) > 0 {
					cmd = fmt.Sprintf("%s -H %s", cmd, host)
				}
				commands = append(commands, podPrefix(pod, cmd))
			}
		}
	}

	return commands
}

func k8sCommands(allCommands []string, pods []string) []string {
	// These commands do not require a pod argument
	var commands = []string{
		"kubectl get nodes -o wide",
		"kubectl describe nodes",
		"kubectl get pods,svc --all-namespaces",
		"kubectl version",
		fmt.Sprintf("kubectl get cm cilium-config -n %s", k8sNamespace),
	}

	// Prepare to run all the commands inside of the pod(s)
	for _, pod := range pods {
		for _, cmd := range allCommands {
			// Add the host flag if set
			if strings.HasPrefix(cmd, "cilium") &&
				!strings.Contains(cmd, "-H") && len(host) > 0 {
				cmd = fmt.Sprintf("%s -H %s", cmd, host)
			}

			if !strings.Contains(cmd, "kubectl exec") {
				cmd = podPrefix(pod, cmd)
			}
			commands = append(commands, cmd)
		}

		// Retrieve current version of pod logs
		cmd := fmt.Sprintf("kubectl -n %s logs --timestamps %s", k8sNamespace, pod)
		commands = append(commands, cmd)

		// Retrieve previous version of pod logs
		cmd = fmt.Sprintf("kubectl -n %s logs --timestamps -p %s", k8sNamespace, pod)
		commands = append(commands, cmd)

		cmd = fmt.Sprintf("kubectl -n %s describe pod %s", k8sNamespace, pod)
		commands = append(commands, cmd)
	}

	if len(pods) == 0 {
		allCommands = append(allCommands, commands...)
		return allCommands
	}

	return commands
}
<commit_msg>bugtool: get bpffs mountpoint from /proc/self/mounts<commit_after>// Copyright 2017-2019 Authors of Cilium
//
// 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 cmd

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/cilium/cilium/pkg/components"
	"github.com/cilium/cilium/pkg/defaults"
	"github.com/cilium/cilium/pkg/mountinfo"
)

// BugtoolConfiguration creates and loads the configuration file used to run
// the commands. The only commands not managed by the configuration is initial
// setup, for ex. searching for Cilium daemonset pods or running uname.
type BugtoolConfiguration struct {
	// Commands is the exact commands that will be run by the bugtool
	Commands []string `json:"commands"`
}

func setupDefaultConfig(path string, k8sPods []string, confDir, cmdDir string) (*BugtoolConfiguration, error) {
	c := BugtoolConfiguration{defaultCommands(confDir, cmdDir, k8sPods)}
	return &c, save(&c, path)
}

func bpffsMountpoint() string {
	mountInfos, err := mountinfo.GetMountInfo()
	if err != nil {
		return ""
	}

	// To determine the mountpoint of the BPF fs we iterate through the list
	// of mount info (i.e. /proc/self/mounts entries) and return the first
	// one which has the "bpf" fs type and the "/" root.
	//
	// The root == "/" condition allows us to ignore all BPF fs which are
	// sub mounts (such as for example /sys/fs/bpf/{xdp, ip, sk, sa}) of the
	// one with the "/" root.
	//
	// Moreover, as Cilium will refuse to start if there are multiple BPF fs
	// which have "/" as their root, we can assume there will be at most one
	// mountpoint which matches the conditions and so we return it as soon
	// as we find it.
	for _, mountInfo := range mountInfos {
		if mountInfo.FilesystemType == "bpf" && mountInfo.Root == "/" {
			return mountInfo.MountPoint
		}
	}

	return ""
}

func defaultCommands(confDir string, cmdDir string, k8sPods []string) []string {
	var commands []string
	// Not expecting all of the commands to be available
	commands = []string{
		// Host and misc
		"ps auxfw",
		"hostname",
		"ip a",
		"ip -4 r",
		"ip -6 r",
		"ip -d -s l",
		"ip -4 n",
		"ip -6 n",
		"ss -t -p -a -i -s",
		"ss -u -p -a -i -s",
		"tc qdisc show",
		"tc -d -s qdisc show",
		"uname -a",
		"top -b -n 1",
		"uptime",
		"dmesg --time-format=iso",
		"sysctl -a",
		"bpftool map show",
		"bpftool prog show",
		// iptables
		"iptables-save -c",
		"iptables -S",
		"ip6tables -S",
		"iptables -L -v",
		"ip rule",
		"ip -4 route show table 2005",
		"ip -6 route show table 2005",
		"ip -4 route show table 200",
		"ip -6 route show table 200",
		// xfrm
		"ip xfrm policy",
		"ip -s xfrm state | awk '!/auth|enc|aead|auth-trunc|comp/'",
		// gops
		fmt.Sprintf("gops memstats $(pidof %s)", components.CiliumAgentName),
		fmt.Sprintf("gops stack $(pidof %s)", components.CiliumAgentName),
		fmt.Sprintf("gops stats $(pidof %s)", components.CiliumAgentName),
		// Get list of open file descriptors managed by the agent
		fmt.Sprintf("ls -la /proc/$(pidof %s)/fd", components.CiliumAgentName),
	}

	if bpffsMountpoint := bpffsMountpoint(); bpffsMountpoint != "" {
		commands = append(commands, []string{
			// LB and CT map for debugging services; using bpftool for a reliable dump
			fmt.Sprintf("bpftool map dump pinned %s/tc/globals/cilium_lb4_services_v2", bpffsMountpoint),
			fmt.Sprintf("bpftool map dump pinned %s/tc/globals/cilium_lb4_services", bpffsMountpoint),
			fmt.Sprintf("bpftool map dump pinned %s/tc/globals/cilium_lb4_backends", bpffsMountpoint),
			fmt.Sprintf("bpftool map dump pinned %s/tc/globals/cilium_lb4_reverse_nat", bpffsMountpoint),
			fmt.Sprintf("bpftool map dump pinned %s/tc/globals/cilium_ct4_global", bpffsMountpoint),
			fmt.Sprintf("bpftool map dump pinned %s/tc/globals/cilium_ct_any4_global", bpffsMountpoint),
			fmt.Sprintf("bpftool map dump pinned %s/tc/globals/cilium_lb4_affinity", bpffsMountpoint),
			fmt.Sprintf("bpftool map dump pinned %s/tc/globals/cilium_lb6_affinity", bpffsMountpoint),
			fmt.Sprintf("bpftool map dump pinned %s/tc/globals/cilium_lb_affinity_match", bpffsMountpoint),
			fmt.Sprintf("bpftool map dump pinned %s/tc/globals/cilium_lb6_services_v2", bpffsMountpoint),
			fmt.Sprintf("bpftool map dump pinned %s/tc/globals/cilium_lb6_services", bpffsMountpoint),
			fmt.Sprintf("bpftool map dump pinned %s/tc/globals/cilium_lb6_backends", bpffsMountpoint),
			fmt.Sprintf("bpftool map dump pinned %s/tc/globals/cilium_lb6_reverse_nat", bpffsMountpoint),
			fmt.Sprintf("bpftool map dump pinned %s/tc/globals/cilium_ct6_global", bpffsMountpoint),
			fmt.Sprintf("bpftool map dump pinned %s/tc/globals/cilium_ct_any6_global", bpffsMountpoint),
			fmt.Sprintf("bpftool map dump pinned %s/tc/globals/cilium_snat_v4_external", bpffsMountpoint),
			fmt.Sprintf("bpftool map dump pinned %s/tc/globals/cilium_snat_v6_external", bpffsMountpoint),
		}...)
	}

	// Commands that require variables and / or more configuration are added
	// separately below
	commands = append(commands, catCommands()...)
	commands = append(commands, ethoolCommands()...)
	commands = append(commands, copyConfigCommands(confDir, k8sPods)...)
	commands = append(commands, copyCiliumInfoCommands(cmdDir, k8sPods)...)

	return k8sCommands(commands, k8sPods)
}

func save(c *BugtoolConfiguration, path string) error {
	f, err := os.Create(path)
	if err != nil {
		return fmt.Errorf("Failed to open file %s for writing: %s", path, err)
	}
	defer f.Close()

	data, err := json.MarshalIndent(c, "", "\t")
	if err != nil {
		return fmt.Errorf("Cannot marshal config %s", err)
	}
	err = ioutil.WriteFile(path, data, 0644)
	if err != nil {
		return fmt.Errorf("Cannot write config %s", err)
	}
	return nil
}

func loadConfigFile(path string) (*BugtoolConfiguration, error) {
	var content []byte
	var err error
	content, err = ioutil.ReadFile(path)

	if err != nil {
		return nil, err
	}

	var c BugtoolConfiguration
	err = json.Unmarshal(content, &c)
	return &c, err
}

func catCommands() []string {
	files := []string{
		"/proc/net/xfrm_stat",
		"/proc/sys/net/core/bpf_jit_enable",
		"/proc/kallsyms",
		"/etc/resolv.conf",
		"/var/log/docker.log",
		"/var/log/daemon.log",
		"/var/log/messages",
	}
	// Only print the files that do exist to reduce number of errors in
	// archive
	commands := []string{}
	for _, f := range files {
		if _, err := os.Stat(f); os.IsNotExist(err) {
			continue
		}
		commands = append(commands, fmt.Sprintf("cat %s", f))
	}
	// TODO: handle K8s case as well.
	return commands
}

func copyConfigCommands(confDir string, k8sPods []string) []string {
	commands := []string{}
	// Location is a convenience structure to avoid too many long lines
	type Location struct {
		Src string
		Dst string
	}

	// These locations don't depend on the kernel version for running so we
	// can add them in this scope.
	locations := []Location{
		{"/proc/config", fmt.Sprintf("%s/kernel-config", confDir)},
		{"/proc/config.gz", fmt.Sprintf("%s/kernel-config.gz", confDir)},
	}

	// The following lines copy the kernel configuration. This code is
	// duplicated for the non Kubernetes case. The variables preventing
	// them to be one block is the pod prefix and namespace used in the
	// path. This should be refactored.
	if len(k8sPods) == 0 {
		kernel, _ := execCommand("uname -r")
		kernel = strings.TrimSpace(kernel)
		// Append the boot config for the current kernel
		l := Location{fmt.Sprintf("/boot/config-%s", kernel),
			fmt.Sprintf("%s/kernel-config-%s", confDir, kernel)}
		locations = append(locations, l)

		// Use the locations to create command strings
		for _, location := range locations {
			if _, err := os.Stat(location.Src); os.IsNotExist(err) {
				continue
			}
			commands = append(commands, fmt.Sprintf("cp %s %s", location.Src, location.Dst))
		}
	} else {
		// If there are multiple pods, we want to get all of the kernel
		// configs. Therefore we need copy commands for all the pods.
		for _, pod := range k8sPods {
			prompt := podPrefix(pod, "uname -r")
			kernel, _ := execCommand(prompt)
			kernel = strings.TrimSpace(kernel)
			l := Location{fmt.Sprintf("/boot/config-%s", kernel),
				fmt.Sprintf("%s/kernel-config-%s", confDir, kernel)}
			locations = append(locations, l)

			// The location is mostly the same but the command is
			// prepended with 'kubectl` and the path contains the
			// namespace and pod. For ex:
			// kubectl cp kube-system/cilium-kg8lv:/tmp/cilium-bugtool-243785589.tar /tmp/cilium-bugtool-243785589.tar
			for _, location := range locations {
				kubectlArg := fmt.Sprintf("%s/%s:%s", k8sNamespace, pod, location.Src)
				cmd := fmt.Sprintf("%s %s %s %s", "kubectl", "cp", kubectlArg, location.Dst)
				commands = append(commands, cmd)
			}
		}
	}
	return commands
}

func copyCiliumInfoCommands(cmdDir string, k8sPods []string) []string {
	// Most of the output should come via debuginfo but also adding
	// these ones for skimming purposes
	ciliumCommands := []string{
		fmt.Sprintf("cilium debuginfo --output=markdown,json -f --output-directory=%s", cmdDir),
		"cilium metrics list",
		"cilium fqdn cache list",
		"cilium config",
		"cilium bpf bandwidth list",
		"cilium bpf tunnel list",
		"cilium bpf lb list",
		"cilium bpf endpoint list",
		"cilium bpf ct list global",
		"cilium bpf nat list",
		"cilium bpf ipmasq list",
		"cilium bpf ipcache list",
		"cilium bpf policy get --all --numeric",
		"cilium bpf sha list",
		"cilium bpf fs show",
		"cilium ip list -o json",
		"cilium map list --verbose",
		"cilium service list",
		"cilium status --verbose",
		"cilium identity list",
		"cilium-health status",
		"cilium policy selectors -o json",
		"cilium node list",
	}
	var commands []string

	stateDir := filepath.Join(defaults.RuntimePath, defaults.StateDir)
	if len(k8sPods) == 0 { // Assuming this is a non k8s deployment
		dst := filepath.Join(cmdDir, defaults.StateDir)
		commands = append(commands, fmt.Sprintf("cp -r %s %s", stateDir, dst))
		for _, cmd := range ciliumCommands {
			// Add the host flag if set
			if len(host) > 0 {
				cmd = fmt.Sprintf("%s -H %s", cmd, host)
			}
			commands = append(commands, cmd)
		}
	} else { // Found k8s pods
		for _, pod := range k8sPods {
			dst := filepath.Join(cmdDir, fmt.Sprintf("%s-%s", pod, defaults.StateDir))
			kubectlArg := fmt.Sprintf("%s/%s:%s", k8sNamespace, pod, stateDir)
			// kubectl cp kube-system/cilium-xrzwr:/var/run/cilium/state cilium-xrzwr-state
			commands = append(commands, fmt.Sprintf("kubectl cp %s %s", kubectlArg, dst))
			for _, cmd := range ciliumCommands {
				// Add the host flag if set
				if len(host) > 0 {
					cmd = fmt.Sprintf("%s -H %s", cmd, host)
				}
				commands = append(commands, podPrefix(pod, cmd))
			}
		}
	}

	return commands
}

func k8sCommands(allCommands []string, pods []string) []string {
	// These commands do not require a pod argument
	var commands = []string{
		"kubectl get nodes -o wide",
		"kubectl describe nodes",
		"kubectl get pods,svc --all-namespaces",
		"kubectl version",
		fmt.Sprintf("kubectl get cm cilium-config -n %s", k8sNamespace),
	}

	// Prepare to run all the commands inside of the pod(s)
	for _, pod := range pods {
		for _, cmd := range allCommands {
			// Add the host flag if set
			if strings.HasPrefix(cmd, "cilium") &&
				!strings.Contains(cmd, "-H") && len(host) > 0 {
				cmd = fmt.Sprintf("%s -H %s", cmd, host)
			}

			if !strings.Contains(cmd, "kubectl exec") {
				cmd = podPrefix(pod, cmd)
			}
			commands = append(commands, cmd)
		}

		// Retrieve current version of pod logs
		cmd := fmt.Sprintf("kubectl -n %s logs --timestamps %s", k8sNamespace, pod)
		commands = append(commands, cmd)

		// Retrieve previous version of pod logs
		cmd = fmt.Sprintf("kubectl -n %s logs --timestamps -p %s", k8sNamespace, pod)
		commands = append(commands, cmd)

		cmd = fmt.Sprintf("kubectl -n %s describe pod %s", k8sNamespace, pod)
		commands = append(commands, cmd)
	}

	if len(pods) == 0 {
		allCommands = append(allCommands, commands...)
		return allCommands
	}

	return commands
}
<|endoftext|>


<commit_before>package model

import (
	"fmt"
	"math"

	"github.com/Konstantin8105/GoFea/dof"
	"github.com/Konstantin8105/GoFea/element"
	"github.com/Konstantin8105/GoFea/finiteElement"
	"github.com/Konstantin8105/GoFea/utils"
	"github.com/Konstantin8105/GoLinAlg/linAlg"
	"github.com/Konstantin8105/GoLinAlg/linAlg/solver"
)

// Solve - solving finite element
func (m *Dim2) Solve() (err error) {

	for caseNumber := 0; caseNumber < len(m.forceCases); caseNumber++ {

		// TODO : check everything
		// TODO : sort  everything
		// TODO : compress loads by number

		// Generate degree of freedom in global system
		var degreeGlobal []dof.AxeNumber
		dofSystem := dof.NewBeam(m.beams, dof.Dim2d)
		for _, beam := range m.beams {
			fe := m.getBeamFiniteElement(beam.Index)
			_, degreeLocal := finiteElement.GetStiffinerGlobalK(fe, &dofSystem, finiteElement.WithoutZeroStiffiner)
			degreeGlobal = append(degreeGlobal, degreeLocal...)
		}
		{
			is := dof.ConvertToInt(degreeGlobal)
			utils.UniqueInt(&is)
			degreeGlobal = dof.ConvertToAxe(is)
		}

		// Create convertor index to axe
		mapIndex := dof.NewMapIndex(&degreeGlobal)

		// Generate global stiffiner matrix [Ko]
		stiffinerKGlobal := m.convertFromLocalToGlobalSystem(&degreeGlobal, &dofSystem, &mapIndex, finiteElement.GetStiffinerGlobalK)

		// Create load vector
		loads := linAlg.NewMatrix64bySize(len(degreeGlobal), 1)
		for _, node := range m.forceCases[caseNumber].nodeForces {
			for _, inx := range node.pointIndexes {
				d := dofSystem.GetDoF(inx)
				if node.nodeForce.Fx != 0.0 {
					h, err := mapIndex.GetByAxe(d[0])
					if err == nil {
						loads.Set(h, 0, node.nodeForce.Fx)
					}
				}
				if node.nodeForce.Fy != 0.0 {
					h, err := mapIndex.GetByAxe(d[1])
					if err == nil {
						loads.Set(h, 0, node.nodeForce.Fy)
					}
				}
				if node.nodeForce.M != 0.0 {
					h, err := mapIndex.GetByAxe(d[2])
					if err == nil {
						loads.Set(h, 0, node.nodeForce.M)
					}
				}
			}
		}

		// Create array degree for support
		// and modify the global stiffiner matrix
		// and load vector
		for _, sup := range m.supports {
			for _, inx := range sup.pointIndexes {
				d := dofSystem.GetDoF(inx)
				var result []dof.AxeNumber
				if sup.support.Dx == true {
					result = append(result, d[0])
				}
				if sup.support.Dy == true {
					result = append(result, d[1])
				}
				if sup.support.M == true {
					result = append(result, d[2])
				}
				// modify stiffiner matrix for correct
				// adding support
				for i := 0; i < len(result); i++ {
					g, err := mapIndex.GetByAxe(result[i])
					if err != nil {
						continue
					}
					for j := 0; j < len(degreeGlobal); j++ {
						h, err := mapIndex.GetByAxe(degreeGlobal[j])
						if err != nil {
							continue
						}
						stiffinerKGlobal.Set(g, h, 0.0)
						stiffinerKGlobal.Set(h, g, 0.0)
					}
					stiffinerKGlobal.Set(g, g, 1.0)
					// modify load vector on support
					loads.Set(g, 0, 0.0)
				}
			}
		}
		//fmt.Println("degreeGlobal = ", degreeGlobal)
		//fmt.Printf("K global = \n%s\n", stiffinerKGlobal)
		//fmt.Printf("Load vector = \n%s\n", loads)

		// Solving system of linear equations for finding
		// the displacement in points in global system
		// TODO: if you have nonlinear elements, then we can use
		// TODO: one global stiffiner matrix for all cases
		lu := solver.NewLUsolver(stiffinerKGlobal)
		x := lu.Solve(loads)
		// TODO: rename global vector of displacement

		fmt.Printf("Global displacement = \n%s\n", x)
		fmt.Println("degreeGlobal = ", degreeGlobal)
		for _, beam := range m.beams {
			fe := m.getBeamFiniteElement(beam.Index)
			klocal, degreeLocal := finiteElement.GetStiffinerGlobalK(fe, &dofSystem, finiteElement.FullInformation)
			fmt.Println("=============")
			fmt.Println("klocalGlobal = ", klocal)
			fmt.Println("degreeLocal = ", degreeLocal)
			globalDisplacement := make([]float64, len(degreeLocal))
			for i := 0; i < len(globalDisplacement); i++ {
				found := false
				for j := 0; j < len(degreeGlobal); j++ {
					if degreeLocal[i] == degreeGlobal[j] {
						found = true
						globalDisplacement[i] = x.Get(j, 0)
						break
					}
				}
				if !found {
					panic("Cannot found dof - MAY BE PINNED. Check")
				}
			}
			fmt.Println("globalDisplacement = ", globalDisplacement)

			t := linAlg.NewMatrix64bySize(10, 10)
			fe.GetCoordinateTransformation(&t)
			fmt.Println("tr.glo --", t)

			// Zo = T_t * Z
			var localDisplacement []float64
			for i := 0; i < t.GetRowSize(); i++ {
				sum := 0.0
				for j := 0; j < t.GetColumnSize(); j++ {
					sum += t.Get(i, j) * globalDisplacement[j]
				}
				localDisplacement = append(localDisplacement, sum)
			}

			fmt.Println("localDisplacement = ", localDisplacement)

			kk := linAlg.NewMatrix64bySize(10, 10)
			fe.GetStiffinerK(&kk)
			fmt.Println("klocalll -->", kk)

			var localForce []float64
			for i := 0; i < kk.GetRowSize(); i++ {
				sum := 0.0
				for j := 0; j < kk.GetRowSize(); j++ {
					sum += kk.Get(i, j) * localDisplacement[j]
				}
				localForce = append(localForce, sum)
			}
			fmt.Println("localForce = ", localForce)
		}

		//TODO: can calculated in parallel local force

		// Generate global mass matrix [Mo]
		n := stiffinerKGlobal.GetRowSize()
		massGlobal := m.convertFromLocalToGlobalSystem(&degreeGlobal, &dofSystem, &mapIndex, finiteElement.GetGlobalMass)
		// m.convertFromLocalToGlobalSystem(&degreeGlobal, &dofSystem, &mapIndex, finiteElement.GetGlobalMass)
		//  linAlg.NewMatrix64bySize(n, n)

		// TODO: Add to matrix mass the nodal mass
		for _, node := range m.forceCases[caseNumber].nodeForces {
			for _, inx := range node.pointIndexes {
				d := dofSystem.GetDoF(inx)
				if node.nodeForce.Fx != 0.0 {
					h, err := mapIndex.GetByAxe(d[0])
					if err == nil {
						massGlobal.Set(h, h, massGlobal.Get(h, h)+math.Abs(node.nodeForce.Fx))
					}
				}
				if node.nodeForce.Fy != 0.0 {
					h, err := mapIndex.GetByAxe(d[1])
					if err == nil {
						massGlobal.Set(h, h, massGlobal.Get(h, h)+math.Abs(node.nodeForce.Fy))
					}
				}
				//if node.nodeForce.M != 0.0 {
				//	h, err := mapIndex.GetByAxe(d[2])
				//	if err == nil {
				//		massGlobal.Set(h, h, massGlobal.Get(h, h)+math.Abs(node.nodeForce.M))
				//		fmt.Println("Add M to mass")
				//	}
				//}
			}
		}

		//TODO: CHECKUING GRAVITY TO MATRIX MASS
		for i := 0; i < massGlobal.GetRowSize(); i++ {
			for j := 0; j < massGlobal.GetColumnSize(); j++ {
				massGlobal.Set(i, j, massGlobal.Get(i, j)/9.806)
			}
		}
		// TODO: ADD to mass WITH OR WITOUT SELFWEIGHT

		// Calculate matrix [H] = [Ko]^-1 * [Mo]
		if stiffinerKGlobal.GetRowSize() != stiffinerKGlobal.GetColumnSize() {
			panic("Not correct size of global stiffiner matrix")
		}
		fmt.Println("GlobalMass = ", massGlobal)
		Ho := linAlg.NewMatrix64bySize(n, n)
		buffer := linAlg.NewMatrix64bySize(n, 1)
		for i := 0; i < n; i++ {
			// Create vertical vector from [Mo]
			for j := 0; j < n; j++ {
				buffer.Set(j, 0, massGlobal.Get(j, i))
			}
			// Calculation
			result := lu.Solve(buffer)
			// Add vector to [Ho]
			for j := 0; j < n; j++ {
				Ho.Set(j, i, result.Get(j, 0))
			}
		}
		fmt.Println("[Ho] = ", Ho)

		// Calculation of natural frequency
		eigen := solver.NewEigen(Ho)
		fmt.Println("lambda       = ", eigen.GetRealEigenvalues())
		fmt.Println("eigenvectors = ", eigen.GetV())
		fmt.Println("getD = ", eigen.GetD())

		value := eigen.GetRealEigenvalues()
		for _, v := range value {
			fmt.Printf("f = %.3v Hz\n", math.Sqrt(1.0/v)/2.0/math.Pi)
		}
	}

	return nil
}

func (m *Dim2) getBeamFiniteElement(inx element.BeamIndex) (fe finiteElement.FiniteElementer) {
	material, err := m.getMaterial(inx)
	if err != nil {
		panic(fmt.Errorf("Cannot found material for beam #%v. Error = %v", inx, err))
	}
	shape, err := m.getShape(inx)
	if err != nil {
		panic(fmt.Errorf("Cannot found shape for beam #%v. Error = %v", inx, err))
	}
	coord, err := m.getCoordinate(inx)
	if err != nil {
		panic(fmt.Errorf("Cannot calculate lenght for beam #%v. Error = %v", inx, err))
	}
	if m.isTruss(inx) {
		f := finiteElement.TrussDim2{
			Material: material,
			Shape:    shape,
			Points:   coord,
		}
		return &f
	} /* else {
		fe := finiteElement.BeamDim2{
			Material: material,
			Shape:    shape,
			Points:   coord,
		}
		err = fe.GetStiffinerK(&buffer)
		if err != nil {
			return err
		}
	}*/
	return nil
}

func (m *Dim2) convertFromLocalToGlobalSystem(degreeGlobal *[]dof.AxeNumber, dofSystem *dof.DoF, mapIndex *dof.MapIndex, f func(finiteElement.FiniteElementer, *dof.DoF, finiteElement.Information) (linAlg.Matrix64, []dof.AxeNumber)) linAlg.Matrix64 {

	globalResult := linAlg.NewMatrix64bySize(len(*degreeGlobal), len(*degreeGlobal))
	for _, beam := range m.beams {
		fe := m.getBeamFiniteElement(beam.Index)
		klocal, degreeLocal := f(fe, dofSystem, finiteElement.WithoutZeroStiffiner)
		// Add local stiffiner matrix to global matrix
		for i := 0; i < len(degreeLocal); i++ {
			g, err := mapIndex.GetByAxe(degreeLocal[i])
			if err != nil {
				continue
			}
			for j := 0; j < len(degreeLocal); j++ {
				h, err := mapIndex.GetByAxe(degreeLocal[j])
				if err != nil {
					continue
				}
				globalResult.Set(g, h, globalResult.Get(g, h)+klocal.Get(i, j))
			}
		}
	}
	return globalResult
}
<commit_msg>add comments<commit_after>package model

import (
	"fmt"
	"math"

	"github.com/Konstantin8105/GoFea/dof"
	"github.com/Konstantin8105/GoFea/element"
	"github.com/Konstantin8105/GoFea/finiteElement"
	"github.com/Konstantin8105/GoFea/utils"
	"github.com/Konstantin8105/GoLinAlg/linAlg"
	"github.com/Konstantin8105/GoLinAlg/linAlg/solver"
)

// Solve - solving finite element
func (m *Dim2) Solve() (err error) {

	for caseNumber := 0; caseNumber < len(m.forceCases); caseNumber++ {

		// TODO : check everything
		// TODO : sort  everything
		// TODO : compress loads by number

		// Generate degree of freedom in global system
		var degreeGlobal []dof.AxeNumber
		dofSystem := dof.NewBeam(m.beams, dof.Dim2d)
		for _, beam := range m.beams {
			fe := m.getBeamFiniteElement(beam.Index)
			_, degreeLocal := finiteElement.GetStiffinerGlobalK(fe, &dofSystem, finiteElement.WithoutZeroStiffiner)
			degreeGlobal = append(degreeGlobal, degreeLocal...)
		}
		{
			is := dof.ConvertToInt(degreeGlobal)
			utils.UniqueInt(&is)
			degreeGlobal = dof.ConvertToAxe(is)
		}

		// Create convertor index to axe
		mapIndex := dof.NewMapIndex(&degreeGlobal)

		// Generate global stiffiner matrix [Ko]
		stiffinerKGlobal := m.convertFromLocalToGlobalSystem(&degreeGlobal, &dofSystem, &mapIndex, finiteElement.GetStiffinerGlobalK)

		// Create load vector
		loads := linAlg.NewMatrix64bySize(len(degreeGlobal), 1)
		for _, node := range m.forceCases[caseNumber].nodeForces {
			for _, inx := range node.pointIndexes {
				d := dofSystem.GetDoF(inx)
				if node.nodeForce.Fx != 0.0 {
					h, err := mapIndex.GetByAxe(d[0])
					if err == nil {
						loads.Set(h, 0, node.nodeForce.Fx)
					}
				}
				if node.nodeForce.Fy != 0.0 {
					h, err := mapIndex.GetByAxe(d[1])
					if err == nil {
						loads.Set(h, 0, node.nodeForce.Fy)
					}
				}
				if node.nodeForce.M != 0.0 {
					h, err := mapIndex.GetByAxe(d[2])
					if err == nil {
						loads.Set(h, 0, node.nodeForce.M)
					}
				}
			}
		}

		// Create array degree for support
		// and modify the global stiffiner matrix
		// and load vector
		for _, sup := range m.supports {
			for _, inx := range sup.pointIndexes {
				d := dofSystem.GetDoF(inx)
				var result []dof.AxeNumber
				if sup.support.Dx == true {
					result = append(result, d[0])
				}
				if sup.support.Dy == true {
					result = append(result, d[1])
				}
				if sup.support.M == true {
					result = append(result, d[2])
				}
				// modify stiffiner matrix for correct
				// adding support
				for i := 0; i < len(result); i++ {
					g, err := mapIndex.GetByAxe(result[i])
					if err != nil {
						continue
					}
					for j := 0; j < len(degreeGlobal); j++ {
						h, err := mapIndex.GetByAxe(degreeGlobal[j])
						if err != nil {
							continue
						}
						stiffinerKGlobal.Set(g, h, 0.0)
						stiffinerKGlobal.Set(h, g, 0.0)
					}
					stiffinerKGlobal.Set(g, g, 1.0)
					// modify load vector on support
					loads.Set(g, 0, 0.0)
				}
			}
		}
		//fmt.Println("degreeGlobal = ", degreeGlobal)
		//fmt.Printf("K global = \n%s\n", stiffinerKGlobal)
		//fmt.Printf("Load vector = \n%s\n", loads)

		// Solving system of linear equations for finding
		// the displacement in points in global system
		// TODO: if you have nonlinear elements, then we can use
		// TODO: one global stiffiner matrix for all cases
		lu := solver.NewLUsolver(stiffinerKGlobal)
		x := lu.Solve(loads)
		// TODO: rename global vector of displacement

		fmt.Printf("Global displacement = \n%s\n", x)
		fmt.Println("degreeGlobal = ", degreeGlobal)
		for _, beam := range m.beams {
			fe := m.getBeamFiniteElement(beam.Index)
			klocal, degreeLocal := finiteElement.GetStiffinerGlobalK(fe, &dofSystem, finiteElement.FullInformation)
			fmt.Println("=============")
			fmt.Println("klocalGlobal = ", klocal)
			fmt.Println("degreeLocal = ", degreeLocal)
			globalDisplacement := make([]float64, len(degreeLocal))
			for i := 0; i < len(globalDisplacement); i++ {
				found := false
				for j := 0; j < len(degreeGlobal); j++ {
					if degreeLocal[i] == degreeGlobal[j] {
						found = true
						globalDisplacement[i] = x.Get(j, 0)
						break
					}
				}
				if !found {
					panic("Cannot found dof - MAY BE PINNED. Check")
				}
			}
			fmt.Println("globalDisplacement = ", globalDisplacement)

			t := linAlg.NewMatrix64bySize(10, 10)
			fe.GetCoordinateTransformation(&t)
			fmt.Println("tr.glo --", t)

			// Zo = T_t * Z
			var localDisplacement []float64
			for i := 0; i < t.GetRowSize(); i++ {
				sum := 0.0
				for j := 0; j < t.GetColumnSize(); j++ {
					sum += t.Get(i, j) * globalDisplacement[j]
				}
				localDisplacement = append(localDisplacement, sum)
			}

			fmt.Println("localDisplacement = ", localDisplacement)

			kk := linAlg.NewMatrix64bySize(10, 10)
			fe.GetStiffinerK(&kk)
			fmt.Println("klocalll -->", kk)

			var localForce []float64
			for i := 0; i < kk.GetRowSize(); i++ {
				sum := 0.0
				for j := 0; j < kk.GetRowSize(); j++ {
					sum += kk.Get(i, j) * localDisplacement[j]
				}
				localForce = append(localForce, sum)
			}
			fmt.Println("localForce = ", localForce)
		}

		//TODO: can calculated in parallel local force

		// Generate global mass matrix [Mo]
		n := stiffinerKGlobal.GetRowSize()
		massGlobal := m.convertFromLocalToGlobalSystem(&degreeGlobal, &dofSystem, &mapIndex, finiteElement.GetGlobalMass)
		// m.convertFromLocalToGlobalSystem(&degreeGlobal, &dofSystem, &mapIndex, finiteElement.GetGlobalMass)
		//  linAlg.NewMatrix64bySize(n, n)

		// TODO: Add to matrix mass the nodal mass
		for _, node := range m.forceCases[caseNumber].nodeForces {
			for _, inx := range node.pointIndexes {
				d := dofSystem.GetDoF(inx)
				if node.nodeForce.Fx != 0.0 {
					h, err := mapIndex.GetByAxe(d[0])
					if err == nil {
						massGlobal.Set(h, h, massGlobal.Get(h, h)+math.Abs(node.nodeForce.Fx))
					}
				}
				if node.nodeForce.Fy != 0.0 {
					h, err := mapIndex.GetByAxe(d[1])
					if err == nil {
						massGlobal.Set(h, h, massGlobal.Get(h, h)+math.Abs(node.nodeForce.Fy))
					}
				}
				// TODO: Moment haven`t mass ???
				// TODO: Check
				//if node.nodeForce.M != 0.0 {
				//	h, err := mapIndex.GetByAxe(d[2])
				//	if err == nil {
				//		massGlobal.Set(h, h, massGlobal.Get(h, h)+math.Abs(node.nodeForce.M))
				//		fmt.Println("Add M to mass")
				//	}
				//}
			}
		}

		//TODO: CHECKUING GRAVITY TO MATRIX MASS
		for i := 0; i < massGlobal.GetRowSize(); i++ {
			for j := 0; j < massGlobal.GetColumnSize(); j++ {
				massGlobal.Set(i, j, massGlobal.Get(i, j)/9.806)
			}
		}
		// TODO: ADD to mass WITH OR WITOUT SELFWEIGHT

		// Calculate matrix [H] = [Ko]^-1 * [Mo]
		if stiffinerKGlobal.GetRowSize() != stiffinerKGlobal.GetColumnSize() {
			panic("Not correct size of global stiffiner matrix")
		}
		fmt.Println("GlobalMass = ", massGlobal)
		Ho := linAlg.NewMatrix64bySize(n, n)
		buffer := linAlg.NewMatrix64bySize(n, 1)
		for i := 0; i < n; i++ {
			// Create vertical vector from [Mo]
			for j := 0; j < n; j++ {
				buffer.Set(j, 0, massGlobal.Get(j, i))
			}
			// Calculation
			result := lu.Solve(buffer)
			// Add vector to [Ho]
			for j := 0; j < n; j++ {
				Ho.Set(j, i, result.Get(j, 0))
			}
		}
		fmt.Println("[Ho] = ", Ho)

		// Calculation of natural frequency
		eigen := solver.NewEigen(Ho)
		fmt.Println("lambda       = ", eigen.GetRealEigenvalues())
		fmt.Println("eigenvectors = ", eigen.GetV())
		fmt.Println("getD = ", eigen.GetD())

		// TODO: fix for avoid strange frequency some is too small or too big
		value := eigen.GetRealEigenvalues()
		for _, v := range value {
			fmt.Printf("f = %.5v Hz\n", math.Sqrt(1.0/v)/2.0/math.Pi)
		}
		// TODO: need add modal mass values for natural frquency calculation
	}

	return nil
}

func (m *Dim2) getBeamFiniteElement(inx element.BeamIndex) (fe finiteElement.FiniteElementer) {
	material, err := m.getMaterial(inx)
	if err != nil {
		panic(fmt.Errorf("Cannot found material for beam #%v. Error = %v", inx, err))
	}
	shape, err := m.getShape(inx)
	if err != nil {
		panic(fmt.Errorf("Cannot found shape for beam #%v. Error = %v", inx, err))
	}
	coord, err := m.getCoordinate(inx)
	if err != nil {
		panic(fmt.Errorf("Cannot calculate lenght for beam #%v. Error = %v", inx, err))
	}
	if m.isTruss(inx) {
		f := finiteElement.TrussDim2{
			Material: material,
			Shape:    shape,
			Points:   coord,
		}
		return &f
	} /* else {
		fe := finiteElement.BeamDim2{
			Material: material,
			Shape:    shape,
			Points:   coord,
		}
		err = fe.GetStiffinerK(&buffer)
		if err != nil {
			return err
		}
	}*/
	return nil
}

func (m *Dim2) convertFromLocalToGlobalSystem(degreeGlobal *[]dof.AxeNumber, dofSystem *dof.DoF, mapIndex *dof.MapIndex, f func(finiteElement.FiniteElementer, *dof.DoF, finiteElement.Information) (linAlg.Matrix64, []dof.AxeNumber)) linAlg.Matrix64 {

	globalResult := linAlg.NewMatrix64bySize(len(*degreeGlobal), len(*degreeGlobal))
	for _, beam := range m.beams {
		fe := m.getBeamFiniteElement(beam.Index)
		klocal, degreeLocal := f(fe, dofSystem, finiteElement.WithoutZeroStiffiner)
		// Add local stiffiner matrix to global matrix
		for i := 0; i < len(degreeLocal); i++ {
			g, err := mapIndex.GetByAxe(degreeLocal[i])
			if err != nil {
				continue
			}
			for j := 0; j < len(degreeLocal); j++ {
				h, err := mapIndex.GetByAxe(degreeLocal[j])
				if err != nil {
					continue
				}
				globalResult.Set(g, h, globalResult.Get(g, h)+klocal.Get(i, j))
			}
		}
	}
	return globalResult
}
<|endoftext|>


<commit_before>package models

import (
	"time"
)

// IssueWatch is connection request for receiving issue notification.
type IssueWatch struct {
	ID          int64     `xorm:"pk autoincr"`
	UserID      int64     `xorm:"UNIQUE(watch) NOT NULL"`
	IssueID     int64     `xorm:"UNIQUE(watch) NOT NULL"`
	IsWatching  bool      `xorm:"NOT NULL"`
	Created     time.Time `xorm:"-"`
	CreatedUnix int64     `xorm:"NOT NULL"`
}

// BeforeInsert is invoked from XORM before inserting an object of this type.
func (iw *IssueWatch) BeforeInsert() {
	iw.Created = time.Now()
	iw.CreatedUnix = time.Now().Unix()
}

// CreateOrUpdateIssueWatch set watching for a user and issue
func CreateOrUpdateIssueWatch(userID, issueID int64, isWatching bool) error {
	iw, exists, err := getIssueWatch(x, userID, issueID)
	if err != nil {
		return err
	}

	if !exists {
		iw = &IssueWatch{
			UserID:     userID,
			IssueID:    issueID,
			IsWatching: isWatching,
		}

		if _, err := x.Insert(iw); err != nil {
			return err
		}
	} else {
		if _, err := x.Table(&IssueWatch{}).Id(iw.ID).Update(map[string]interface{}{"is_watching": isWatching}); err != nil {
			return err
		}
	}
	return nil
}

// GetIssueWatch returns an issue watch by user and issue
func GetIssueWatch(userID, issueID int64) (iw *IssueWatch, exists bool, err error) {
	iw, exists, err = getIssueWatch(x, userID, issueID)
	return
}
func getIssueWatch(e Engine, userID, issueID int64) (iw *IssueWatch, exists bool, err error) {
	iw = new(IssueWatch)
	exists, err = e.
		Where("user_id = ?", userID).
		And("issue_id = ?", issueID).
		Get(iw)
	return
}

func GetIssueWatchers(issueID int64) ([]*IssueWatch, error) {
	return getIssueWatchers(x, issueID)
}
func getIssueWatchers(e Engine, issueID int64) (watches []*IssueWatch, err error) {
	err = e.
		Where("issue_id = ?", issueID).
		Find(&watches)
	return
}
<commit_msg>Add updated_unix column on issue_watch<commit_after>package models

import (
	"time"
)

// IssueWatch is connection request for receiving issue notification.
type IssueWatch struct {
	ID          int64     `xorm:"pk autoincr"`
	UserID      int64     `xorm:"UNIQUE(watch) NOT NULL"`
	IssueID     int64     `xorm:"UNIQUE(watch) NOT NULL"`
	IsWatching  bool      `xorm:"NOT NULL"`
	Created     time.Time `xorm:"-"`
	CreatedUnix int64     `xorm:"NOT NULL"`
	Updated     time.Time `xorm:"-"`
	UpdatedUnix int64     `xorm:"NOT NULL"`
}

// BeforeInsert is invoked from XORM before inserting an object of this type.
func (iw *IssueWatch) BeforeInsert() {
	iw.Created = time.Now()
	iw.CreatedUnix = time.Now().Unix()
	iw.Updated = time.Now()
	iw.UpdatedUnix = time.Now().Unix()
}

func (iw *IssueWatch) BeforeUpdate() {
	iw.Updated = time.Now()
	iw.UpdatedUnix = time.Now().Unix()
}

// CreateOrUpdateIssueWatch set watching for a user and issue
func CreateOrUpdateIssueWatch(userID, issueID int64, isWatching bool) error {
	iw, exists, err := getIssueWatch(x, userID, issueID)
	if err != nil {
		return err
	}

	if !exists {
		iw = &IssueWatch{
			UserID:     userID,
			IssueID:    issueID,
			IsWatching: isWatching,
		}

		if _, err := x.Insert(iw); err != nil {
			return err
		}
	} else {
		iw.IsWatching = isWatching

		if _, err := x.Id(iw.ID).Cols("is_watching", "updated_unix").Update(iw); err != nil {
			return err
		}
	}
	return nil
}

// GetIssueWatch returns an issue watch by user and issue
func GetIssueWatch(userID, issueID int64) (iw *IssueWatch, exists bool, err error) {
	iw, exists, err = getIssueWatch(x, userID, issueID)
	return
}
func getIssueWatch(e Engine, userID, issueID int64) (iw *IssueWatch, exists bool, err error) {
	iw = new(IssueWatch)
	exists, err = e.
		Where("user_id = ?", userID).
		And("issue_id = ?", issueID).
		Get(iw)
	return
}

func GetIssueWatchers(issueID int64) ([]*IssueWatch, error) {
	return getIssueWatchers(x, issueID)
}
func getIssueWatchers(e Engine, issueID int64) (watches []*IssueWatch, err error) {
	err = e.
		Where("issue_id = ?", issueID).
		Find(&watches)
	return
}
<|endoftext|>


<commit_before>package models

import (
	"bufio"
	"crypto/md5"
	"crypto/sha256"
	"fmt"
	"github.com/APTrust/exchange/constants"
	"github.com/APTrust/exchange/platform"
	"github.com/APTrust/exchange/util"
	"github.com/APTrust/exchange/util/fileutil"
	"github.com/nu7hatch/gouuid"
	"hash"
	"io"
	"path"
	"regexp"
	"strings"
	"time"
)

// VirtualBag creates an IntellectualObject from a bag on disk.
// The IntellectualObject can then be validated by workers.BagValidator.
type VirtualBag struct {
	pathToBag        string
	calculateMd5     bool
	calculateSha256  bool
	tagFilesToParse  []string
	obj              *IntellectualObject
	summary          *WorkSummary
	readIterator     fileutil.ReadIterator
}

// NewVirtualBag creates a new virtual bag. Param pathToBag should
// be an absolute path to either a tar file or a directory containing
// an untarred bag. It pathToBag points to a tar file, the Read()
// function will read the bag without untarring it. Param tagFilesToParse
// should be a list of relative paths, pointing to tag files within the
// bag that should be parsed. For example, "aptrust-info.txt" or
// "dpn_tags/dpn-info.txt" Params calculateMd5 and calculateSha256
// indicate whether we should calculate md5 and/or sha256 checksums
// on the files in the bag.
func NewVirtualBag(pathToBag string, tagFilesToParse []string, calculateMd5, calculateSha256 bool) (*VirtualBag) {
	if tagFilesToParse == nil {
		tagFilesToParse = make([]string, 0)
	}
	return &VirtualBag{
		calculateMd5: calculateMd5,
		calculateSha256: calculateSha256,
		pathToBag: pathToBag,
		tagFilesToParse: tagFilesToParse,
	}
}

// Read() reads the bag and returns an IntellectualObject and a WorkSummary.
// The WorkSummary will include a list of errors, if there were any.
// The list of files contained in IntellectualObject.GenericFiles will include
// ALL files found in the bag, even some we may not want to save, such as
// those beginning with dots and dashes. If you don't want to preserve those
// files you can delete them from the IntellectualObject manually later.
func (vbag *VirtualBag) Read() (*IntellectualObject, *WorkSummary) {
	vbag.summary = NewWorkSummary()
	vbag.summary.Start()
	vbag.obj = NewIntellectualObject()
	vbag.obj.Identifier, _ = util.CleanBagName(path.Base(vbag.pathToBag))
	if strings.HasSuffix(vbag.pathToBag, ".tar") {
		vbag.obj.IngestTarFilePath = vbag.pathToBag
	} else {
		vbag.obj.IngestUntarredPath = vbag.pathToBag
	}

	// Compile a list of the bag's contents (GenericFiles),
	// and calculate checksums for everything in the bag.
	var err error
	if vbag.obj.IngestTarFilePath != "" {
		vbag.readIterator, err = fileutil.NewTarFileIterator(vbag.obj.IngestTarFilePath)
	} else {
		vbag.readIterator, err = fileutil.NewFileSystemIterator(vbag.obj.IngestUntarredPath)
	}
	if err != nil {
		vbag.summary.AddError("Could not read bag: %v", err)
	} else {
		vbag.addGenericFiles()
	}

	// Golang's tar file reader is forward-only, so we need to
	// open a new iterator to read through a handful of tag files,
	// manifests and tag manifests.
	vbag.readIterator = nil
	if vbag.obj.IngestTarFilePath != "" {
		vbag.readIterator, err = fileutil.NewTarFileIterator(vbag.obj.IngestTarFilePath)
	} else {
		vbag.readIterator, err = fileutil.NewFileSystemIterator(vbag.obj.IngestUntarredPath)
	}
	if err != nil {
		vbag.summary.AddError("Could not read bag: %v", err)
	} else {
		vbag.parseManifestsAndTagFiles()
	}
	vbag.summary.Finish()
	return vbag.obj, vbag.summary
}

// Add every file in the bag to the list of generic files.
func (vbag *VirtualBag) addGenericFiles() () {
	for {
		err := vbag.addGenericFile()
		if err == io.EOF {
			break
		} else if err != nil {
			vbag.summary.AddError(err.Error())
		}
	}
}

// Adds a single generic file to the bag.
func (vbag *VirtualBag) addGenericFile() (error) {
	reader, fileSummary, err := vbag.readIterator.Next()
	if err != nil {
		return err
	}
	if !fileSummary.IsRegularFile {
		return nil
	}
	uuid, err := uuid.NewV4()
	if err != nil {
		panic("Can't read from /dev/urandom!")
	}
	gf := NewGenericFile()
	gf.Identifier = fmt.Sprintf("%s/%s", vbag.obj.Identifier, fileSummary.RelPath)
	gf.IntellectualObjectIdentifier = vbag.obj.Identifier
	gf.Size = fileSummary.Size
	gf.FileModified = fileSummary.ModTime
	gf.IngestLocalPath = fileSummary.AbsPath // will be empty if bag is tarred
	gf.IngestUUID = uuid.String()
	gf.IngestUUIDGeneratedAt = time.Now().UTC()
	gf.IngestFileUid = fileSummary.Uid
	gf.IngestFileGid = fileSummary.Gid
	vbag.obj.GenericFiles = append(vbag.obj.GenericFiles, gf)
	vbag.setIngestFileType(gf, fileSummary)
	return vbag.calculateChecksums(reader, gf)
}

// Figure out what type of file this is.
func (vbag *VirtualBag) setIngestFileType(gf *GenericFile, fileSummary *fileutil.FileSummary) {
	if strings.HasPrefix(fileSummary.RelPath, "tagmanifest-") {
		gf.IngestFileType = constants.TAG_MANIFEST
		vbag.obj.IngestTagManifests = append(vbag.obj.IngestTagManifests, fileSummary.RelPath)
	} else if strings.HasPrefix(fileSummary.RelPath, "manifest-") {
		gf.IngestFileType = constants.PAYLOAD_MANIFEST
		vbag.obj.IngestManifests = append(vbag.obj.IngestManifests, fileSummary.RelPath)
	} else if strings.HasPrefix(fileSummary.RelPath, "data/") {
		gf.IngestFileType = constants.PAYLOAD_FILE
	} else {
		gf.IngestFileType = constants.TAG_FILE
	}
}

// Calculate the md5 and/or sha256 checksums on a file.
func (vbag *VirtualBag) calculateChecksums(reader io.Reader, gf *GenericFile) (error) {
	hashes := make([]io.Writer, 0)
	var md5Hash hash.Hash
	var sha256Hash hash.Hash
	if vbag.calculateMd5 {
		md5Hash = md5.New()
		hashes = append(hashes, md5Hash)
	}
	if vbag.calculateSha256 {
		sha256Hash = sha256.New()
		hashes = append(hashes, sha256Hash)
	}
	if len(hashes) > 0 {
		multiWriter := io.MultiWriter(hashes...)
		io.Copy(multiWriter, reader)
		utcNow := time.Now().UTC()
		if md5Hash != nil {
			gf.IngestMd5 = fmt.Sprintf("%x", md5Hash.Sum(nil))
			gf.IngestMd5GeneratedAt = utcNow
		}
		if sha256Hash != nil {
			gf.IngestSha256 = fmt.Sprintf("%x", sha256Hash.Sum(nil))
			gf.IngestSha256GeneratedAt = utcNow
		}
	}
	// on err, defaults to application/binary
	buf := make([]byte, 1024)
	_, _ = reader.Read(buf)
	gf.FileFormat, _ = platform.GuessMimeTypeByBuffer(buf)
	return nil
}

func (vbag *VirtualBag) parseManifestsAndTagFiles() {
	for {
		reader, fileSummary, err := vbag.readIterator.Next()
		if reader != nil {
			defer reader.Close()
		}
		if err == io.EOF {
			return
		}
		if err != nil {
			vbag.summary.AddError(err.Error())
			continue
		}
		if util.StringListContains(vbag.tagFilesToParse, fileSummary.RelPath) {
			vbag.parseTags(reader, fileSummary.RelPath)
		} else if util.StringListContains(vbag.obj.IngestManifests, fileSummary.RelPath) ||
			util.StringListContains(vbag.obj.IngestTagManifests, fileSummary.RelPath) {
			vbag.parseManifest(reader, fileSummary.RelPath)
		}
	}
}

// Parse the checksums in a manifest.
func (vbag *VirtualBag) parseManifest(reader io.Reader, relFilePath string) () {
	alg := constants.AlgMd5
	if strings.Contains(relFilePath, constants.AlgSha256) {
		alg = constants.AlgSha256
	}
	re := regexp.MustCompile(`^(\S*)\s*(.*)`)
	scanner := bufio.NewScanner(reader)
	lineNum := 1
	for scanner.Scan() {
		line := scanner.Text()
		if re.MatchString(line) {
			data := re.FindStringSubmatch(line)
			digest := data[1]
			filePath := data[2]
			genericFile := vbag.obj.FindGenericFileByPath(filePath)
			if genericFile == nil {
				vbag.summary.AddError(
					"Manifest '%s' includes checksum for file '%s', which was not found in bag",
					relFilePath, filePath)
				continue
			}
			if alg == constants.AlgMd5 {
				genericFile.IngestManifestMd5 = digest
			} else if alg == constants.AlgSha256 {
				genericFile.IngestManifestSha256 = digest
			}
		} else {
			vbag.summary.AddError(fmt.Sprintf(
				"Unable to parse data from line %d of manifest %s: %s",
				lineNum, relFilePath, line))
		}
		lineNum += 1
	}
}

// Parse the tag fields in a file.
func (vbag *VirtualBag) parseTags(reader io.Reader, relFilePath string) () {
	re := regexp.MustCompile(`^(\S*\:)?(\s.*)?$`)
	scanner := bufio.NewScanner(reader)
	var tag *Tag
	for scanner.Scan() {
		line := scanner.Text()
		if re.MatchString(line) {
			data := re.FindStringSubmatch(line)
			data[1] = strings.Replace(data[1], ":", "", 1)
			if data[1] != "" {
				if tag != nil && tag.Label != "" {
					vbag.obj.IngestTags = append(vbag.obj.IngestTags, tag)
				}
				tag = NewTag(relFilePath, data[1], strings.Trim(data[2], " "))
				vbag.setIntelObjTagValue(tag)
				continue
			}
			value := strings.Trim(data[2], " ")
			tag.Value = strings.Join([]string{tag.Value, value}, " ")
			vbag.setIntelObjTagValue(tag)
		} else {
			vbag.summary.AddError("Unable to parse tag data from line: %s", line)
		}
	}
	if tag.Label != "" {
		vbag.obj.IngestTags = append(vbag.obj.IngestTags, tag)
	}
	if scanner.Err() != nil {
		vbag.summary.AddError("Error reading tag file '%s': %v",
			relFilePath, scanner.Err().Error())
	}
}

// Copy certain values from the aptrust-info.txt file into
// properties of the IntellectualObject.
func (vbag *VirtualBag) setIntelObjTagValue(tag *Tag) () {
	if tag.SourceFile == "aptrust-info.txt" {
		label := strings.ToLower(tag.Label)
		switch label {
		case "title": vbag.obj.Title = tag.Value
		case "description": vbag.obj.Description = tag.Value
		case "access": vbag.obj.Access = tag.Value
		case "internal-sender-identifier": vbag.obj.AltIdentifier = tag.Value
		}
	}
}
<commit_msg>Assign description and alt_identifier<commit_after>package models

import (
	"bufio"
	"crypto/md5"
	"crypto/sha256"
	"fmt"
	"github.com/APTrust/exchange/constants"
	"github.com/APTrust/exchange/platform"
	"github.com/APTrust/exchange/util"
	"github.com/APTrust/exchange/util/fileutil"
	"github.com/nu7hatch/gouuid"
	"hash"
	"io"
	"path"
	"regexp"
	"strings"
	"time"
)

// VirtualBag creates an IntellectualObject from a bag on disk.
// The IntellectualObject can then be validated by workers.BagValidator.
type VirtualBag struct {
	pathToBag        string
	calculateMd5     bool
	calculateSha256  bool
	tagFilesToParse  []string
	obj              *IntellectualObject
	summary          *WorkSummary
	readIterator     fileutil.ReadIterator
}

// NewVirtualBag creates a new virtual bag. Param pathToBag should
// be an absolute path to either a tar file or a directory containing
// an untarred bag. It pathToBag points to a tar file, the Read()
// function will read the bag without untarring it. Param tagFilesToParse
// should be a list of relative paths, pointing to tag files within the
// bag that should be parsed. For example, "aptrust-info.txt" or
// "dpn_tags/dpn-info.txt" Params calculateMd5 and calculateSha256
// indicate whether we should calculate md5 and/or sha256 checksums
// on the files in the bag.
func NewVirtualBag(pathToBag string, tagFilesToParse []string, calculateMd5, calculateSha256 bool) (*VirtualBag) {
	if tagFilesToParse == nil {
		tagFilesToParse = make([]string, 0)
	}
	return &VirtualBag{
		calculateMd5: calculateMd5,
		calculateSha256: calculateSha256,
		pathToBag: pathToBag,
		tagFilesToParse: tagFilesToParse,
	}
}

// Read() reads the bag and returns an IntellectualObject and a WorkSummary.
// The WorkSummary will include a list of errors, if there were any.
// The list of files contained in IntellectualObject.GenericFiles will include
// ALL files found in the bag, even some we may not want to save, such as
// those beginning with dots and dashes. If you don't want to preserve those
// files you can delete them from the IntellectualObject manually later.
func (vbag *VirtualBag) Read() (*IntellectualObject, *WorkSummary) {
	vbag.summary = NewWorkSummary()
	vbag.summary.Start()
	vbag.obj = NewIntellectualObject()
	vbag.obj.Identifier, _ = util.CleanBagName(path.Base(vbag.pathToBag))
	if strings.HasSuffix(vbag.pathToBag, ".tar") {
		vbag.obj.IngestTarFilePath = vbag.pathToBag
	} else {
		vbag.obj.IngestUntarredPath = vbag.pathToBag
	}

	// Compile a list of the bag's contents (GenericFiles),
	// and calculate checksums for everything in the bag.
	var err error
	if vbag.obj.IngestTarFilePath != "" {
		vbag.readIterator, err = fileutil.NewTarFileIterator(vbag.obj.IngestTarFilePath)
	} else {
		vbag.readIterator, err = fileutil.NewFileSystemIterator(vbag.obj.IngestUntarredPath)
	}
	if err != nil {
		vbag.summary.AddError("Could not read bag: %v", err)
	} else {
		vbag.addGenericFiles()
	}

	// Golang's tar file reader is forward-only, so we need to
	// open a new iterator to read through a handful of tag files,
	// manifests and tag manifests.
	vbag.readIterator = nil
	if vbag.obj.IngestTarFilePath != "" {
		vbag.readIterator, err = fileutil.NewTarFileIterator(vbag.obj.IngestTarFilePath)
	} else {
		vbag.readIterator, err = fileutil.NewFileSystemIterator(vbag.obj.IngestUntarredPath)
	}
	if err != nil {
		vbag.summary.AddError("Could not read bag: %v", err)
	} else {
		vbag.parseManifestsAndTagFiles()
	}
	vbag.summary.Finish()
	return vbag.obj, vbag.summary
}

// Add every file in the bag to the list of generic files.
func (vbag *VirtualBag) addGenericFiles() () {
	for {
		err := vbag.addGenericFile()
		if err == io.EOF {
			break
		} else if err != nil {
			vbag.summary.AddError(err.Error())
		}
	}
}

// Adds a single generic file to the bag.
func (vbag *VirtualBag) addGenericFile() (error) {
	reader, fileSummary, err := vbag.readIterator.Next()
	if err != nil {
		return err
	}
	if !fileSummary.IsRegularFile {
		return nil
	}
	uuid, err := uuid.NewV4()
	if err != nil {
		panic("Can't read from /dev/urandom!")
	}
	gf := NewGenericFile()
	gf.Identifier = fmt.Sprintf("%s/%s", vbag.obj.Identifier, fileSummary.RelPath)
	gf.IntellectualObjectIdentifier = vbag.obj.Identifier
	gf.Size = fileSummary.Size
	gf.FileModified = fileSummary.ModTime
	gf.IngestLocalPath = fileSummary.AbsPath // will be empty if bag is tarred
	gf.IngestUUID = uuid.String()
	gf.IngestUUIDGeneratedAt = time.Now().UTC()
	gf.IngestFileUid = fileSummary.Uid
	gf.IngestFileGid = fileSummary.Gid
	vbag.obj.GenericFiles = append(vbag.obj.GenericFiles, gf)
	vbag.setIngestFileType(gf, fileSummary)
	return vbag.calculateChecksums(reader, gf)
}

// Figure out what type of file this is.
func (vbag *VirtualBag) setIngestFileType(gf *GenericFile, fileSummary *fileutil.FileSummary) {
	if strings.HasPrefix(fileSummary.RelPath, "tagmanifest-") {
		gf.IngestFileType = constants.TAG_MANIFEST
		vbag.obj.IngestTagManifests = append(vbag.obj.IngestTagManifests, fileSummary.RelPath)
	} else if strings.HasPrefix(fileSummary.RelPath, "manifest-") {
		gf.IngestFileType = constants.PAYLOAD_MANIFEST
		vbag.obj.IngestManifests = append(vbag.obj.IngestManifests, fileSummary.RelPath)
	} else if strings.HasPrefix(fileSummary.RelPath, "data/") {
		gf.IngestFileType = constants.PAYLOAD_FILE
	} else {
		gf.IngestFileType = constants.TAG_FILE
	}
}

// Calculate the md5 and/or sha256 checksums on a file.
func (vbag *VirtualBag) calculateChecksums(reader io.Reader, gf *GenericFile) (error) {
	hashes := make([]io.Writer, 0)
	var md5Hash hash.Hash
	var sha256Hash hash.Hash
	if vbag.calculateMd5 {
		md5Hash = md5.New()
		hashes = append(hashes, md5Hash)
	}
	if vbag.calculateSha256 {
		sha256Hash = sha256.New()
		hashes = append(hashes, sha256Hash)
	}
	if len(hashes) > 0 {
		multiWriter := io.MultiWriter(hashes...)
		io.Copy(multiWriter, reader)
		utcNow := time.Now().UTC()
		if md5Hash != nil {
			gf.IngestMd5 = fmt.Sprintf("%x", md5Hash.Sum(nil))
			gf.IngestMd5GeneratedAt = utcNow
		}
		if sha256Hash != nil {
			gf.IngestSha256 = fmt.Sprintf("%x", sha256Hash.Sum(nil))
			gf.IngestSha256GeneratedAt = utcNow
		}
	}
	// on err, defaults to application/binary
	buf := make([]byte, 1024)
	_, _ = reader.Read(buf)
	gf.FileFormat, _ = platform.GuessMimeTypeByBuffer(buf)
	return nil
}

func (vbag *VirtualBag) parseManifestsAndTagFiles() {
	for {
		reader, fileSummary, err := vbag.readIterator.Next()
		if reader != nil {
			defer reader.Close()
		}
		if err == io.EOF {
			return
		}
		if err != nil {
			vbag.summary.AddError(err.Error())
			continue
		}
		if util.StringListContains(vbag.tagFilesToParse, fileSummary.RelPath) {
			vbag.parseTags(reader, fileSummary.RelPath)
		} else if util.StringListContains(vbag.obj.IngestManifests, fileSummary.RelPath) ||
			util.StringListContains(vbag.obj.IngestTagManifests, fileSummary.RelPath) {
			vbag.parseManifest(reader, fileSummary.RelPath)
		}
	}
}

// Parse the checksums in a manifest.
func (vbag *VirtualBag) parseManifest(reader io.Reader, relFilePath string) () {
	alg := constants.AlgMd5
	if strings.Contains(relFilePath, constants.AlgSha256) {
		alg = constants.AlgSha256
	}
	re := regexp.MustCompile(`^(\S*)\s*(.*)`)
	scanner := bufio.NewScanner(reader)
	lineNum := 1
	for scanner.Scan() {
		line := scanner.Text()
		if re.MatchString(line) {
			data := re.FindStringSubmatch(line)
			digest := data[1]
			filePath := data[2]
			genericFile := vbag.obj.FindGenericFileByPath(filePath)
			if genericFile == nil {
				vbag.summary.AddError(
					"Manifest '%s' includes checksum for file '%s', which was not found in bag",
					relFilePath, filePath)
				continue
			}
			if alg == constants.AlgMd5 {
				genericFile.IngestManifestMd5 = digest
			} else if alg == constants.AlgSha256 {
				genericFile.IngestManifestSha256 = digest
			}
		} else {
			vbag.summary.AddError(fmt.Sprintf(
				"Unable to parse data from line %d of manifest %s: %s",
				lineNum, relFilePath, line))
		}
		lineNum += 1
	}
}

// Parse the tag fields in a file.
func (vbag *VirtualBag) parseTags(reader io.Reader, relFilePath string) () {
	re := regexp.MustCompile(`^(\S*\:)?(\s.*)?$`)
	scanner := bufio.NewScanner(reader)
	var tag *Tag
	for scanner.Scan() {
		line := scanner.Text()
		if re.MatchString(line) {
			data := re.FindStringSubmatch(line)
			data[1] = strings.Replace(data[1], ":", "", 1)
			if data[1] != "" {
				if tag != nil && tag.Label != "" {
					vbag.obj.IngestTags = append(vbag.obj.IngestTags, tag)
				}
				tag = NewTag(relFilePath, data[1], strings.Trim(data[2], " "))
				vbag.setIntelObjTagValue(tag)
				continue
			}
			value := strings.Trim(data[2], " ")
			tag.Value = strings.Join([]string{tag.Value, value}, " ")
			vbag.setIntelObjTagValue(tag)
		} else {
			vbag.summary.AddError("Unable to parse tag data from line: %s", line)
		}
	}
	if tag.Label != "" {
		vbag.obj.IngestTags = append(vbag.obj.IngestTags, tag)
	}
	if scanner.Err() != nil {
		vbag.summary.AddError("Error reading tag file '%s': %v",
			relFilePath, scanner.Err().Error())
	}
}

// Copy certain values from the aptrust-info.txt file into
// properties of the IntellectualObject.
func (vbag *VirtualBag) setIntelObjTagValue(tag *Tag) () {
	if tag.SourceFile == "aptrust-info.txt" {
		label := strings.ToLower(tag.Label)
		switch label {
		case "title": vbag.obj.Title = tag.Value
		case "access": vbag.obj.Access = tag.Value
		}
	} else if tag.SourceFile == "bag-info.txt" {
		label := strings.ToLower(tag.Label)
		switch label {
		case "internal-sender-description": vbag.obj.Description = tag.Value
		case "internal-sender-identifier": vbag.obj.AltIdentifier = tag.Value
		}
	}

}
<|endoftext|>