mirror of
https://github.com/redhat-developer/odo.git
synced 2025-10-19 03:06:19 +03:00
Ignore devstate when existing process name is not odo + delete devstate files with odo delete component (#7090)
* Ignore devstate when existing process name is not odo * Delete orphan devstate files with odo delete component * Update unit tests * Create fake system * Add unit tests for odo delete component * Integration tests for odo dev * Troubleshooting * First process on Windows is 4 * Use go-ps lib for pidExists
This commit is contained in:
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/redhat-developer/odo/pkg/config"
|
||||
envcontext "github.com/redhat-developer/odo/pkg/config/context"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli"
|
||||
odocontext "github.com/redhat-developer/odo/pkg/odo/context"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset"
|
||||
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||
)
|
||||
@@ -67,6 +68,7 @@ func runCommand(
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx = envcontext.WithEnvConfig(ctx, *envConfig)
|
||||
ctx = odocontext.WithPID(ctx, 101)
|
||||
|
||||
for k, v := range options.env {
|
||||
t.Setenv(k, v)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -14,8 +16,10 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
"github.com/redhat-developer/odo/pkg/odo/commonflags"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset"
|
||||
"github.com/redhat-developer/odo/pkg/podman"
|
||||
"github.com/redhat-developer/odo/pkg/state"
|
||||
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||
"github.com/redhat-developer/odo/pkg/util"
|
||||
"github.com/redhat-developer/odo/tests/helper"
|
||||
@@ -97,6 +101,7 @@ var nodeJsSourcesFsContent fscontentFunc = func(fs filesystem.Filesystem) error
|
||||
type fsOptions struct {
|
||||
dotOdoExists bool
|
||||
generated []string
|
||||
devstateFiles []state.Content
|
||||
}
|
||||
|
||||
var nodeJsSourcesAndDevfileFsContent = func(devfilePath string, options fsOptions) fscontentFunc {
|
||||
@@ -114,6 +119,21 @@ var nodeJsSourcesAndDevfileFsContent = func(devfilePath string, options fsOption
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
|
||||
}
|
||||
for _, devstateFile := range options.devstateFiles {
|
||||
jsonContent, err := json.MarshalIndent(devstateFile, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dir := filepath.Dir(util.DotOdoDirectory)
|
||||
err = os.MkdirAll(dir, 0750)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = fs.WriteFile(fmt.Sprintf("./.odo/devstate.%d.json", devstateFile.PID), jsonContent, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -250,9 +270,9 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
||||
nameOptions map[string]nameOption
|
||||
|
||||
wantErr string
|
||||
checkOutput func(t *testing.T, s string)
|
||||
checkOutput func(t *testing.T, testContext testContext, s string)
|
||||
checkFS func(t *testing.T, fs filesystem.Filesystem)
|
||||
checkCalls func(t *testing.T, clientset clientset.Clientset, tetsContext testContext)
|
||||
checkCalls func(t *testing.T, clientset clientset.Clientset, testContext testContext)
|
||||
}{
|
||||
{
|
||||
name: "delete component when Devfile is not present in the directory",
|
||||
@@ -324,7 +344,7 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
||||
"no": noNameOptions,
|
||||
},
|
||||
|
||||
checkOutput: func(t *testing.T, s string) {
|
||||
checkOutput: func(t *testing.T, testContext testContext, s string) {
|
||||
gomega.Expect(s).ToNot(gomega.ContainSubstring("devfile.yaml"), "should not list the devfile.yaml")
|
||||
},
|
||||
checkFS: func(t *testing.T, fs filesystem.Filesystem) {
|
||||
@@ -353,7 +373,7 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
||||
"no": noNameOptions,
|
||||
},
|
||||
|
||||
checkOutput: func(t *testing.T, s string) {
|
||||
checkOutput: func(t *testing.T, testContext testContext, s string) {
|
||||
gomega.Expect(s).To(gomega.ContainSubstring("devfile.yaml"), "should list the devfile.yaml")
|
||||
},
|
||||
checkFS: func(t *testing.T, fs filesystem.Filesystem) {
|
||||
@@ -379,6 +399,17 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
||||
fsOptions{
|
||||
generated: []string{"devfile.yaml"},
|
||||
}),
|
||||
"nodeJS sources, Devfile and orphan devstate file": nodeJsSourcesAndDevfileFsContent(
|
||||
filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"),
|
||||
fsOptions{
|
||||
dotOdoExists: true,
|
||||
devstateFiles: []state.Content{
|
||||
{
|
||||
PID: 43,
|
||||
Platform: commonflags.PlatformPodman,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
runningInOptions: allRunningInOptions,
|
||||
filesOptions: allFilesOptions,
|
||||
@@ -386,8 +417,14 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
||||
"no": noNameOptions,
|
||||
},
|
||||
|
||||
checkOutput: func(t *testing.T, s string) {
|
||||
checkOutput: func(t *testing.T, testContext testContext, s string) {
|
||||
gomega.Expect(s).To(gomega.ContainSubstring("No resource found for component %q", "my-component"))
|
||||
if testContext.fscontent == "nodeJS sources, Devfile and orphan devstate file" &&
|
||||
testContext.runningInOption != "deploy" {
|
||||
gomega.Expect(s).To(gomega.ContainSubstring("devstate.43.json"))
|
||||
} else {
|
||||
gomega.Expect(s).To(gomega.Not(gomega.ContainSubstring("devstate.43.json")))
|
||||
}
|
||||
},
|
||||
checkCalls: checkCallsNonDeployedComponent,
|
||||
},
|
||||
@@ -411,6 +448,17 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
||||
fsOptions{
|
||||
generated: []string{"devfile.yaml"},
|
||||
}),
|
||||
"nodeJS sources, Devfile and orphan devstate file": nodeJsSourcesAndDevfileFsContent(
|
||||
filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"),
|
||||
fsOptions{
|
||||
dotOdoExists: true,
|
||||
devstateFiles: []state.Content{
|
||||
{
|
||||
PID: 43,
|
||||
Platform: commonflags.PlatformPodman,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
runningInOptions: map[string]runningInOption{
|
||||
"no": noRunningInOption,
|
||||
@@ -421,9 +469,15 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
||||
"no": noNameOptions,
|
||||
},
|
||||
|
||||
checkOutput: func(t *testing.T, s string) {
|
||||
checkOutput: func(t *testing.T, testContext testContext, s string) {
|
||||
gomega.Expect(s).To(gomega.ContainSubstring("The following pods and associated volumes will get deleted from podman"))
|
||||
gomega.Expect(s).To(gomega.ContainSubstring("- my-component-app"))
|
||||
if testContext.fscontent == "nodeJS sources, Devfile and orphan devstate file" &&
|
||||
testContext.runningInOption != "deploy" {
|
||||
gomega.Expect(s).To(gomega.ContainSubstring("devstate.43.json"))
|
||||
} else {
|
||||
gomega.Expect(s).To(gomega.Not(gomega.ContainSubstring("devstate.43.json")))
|
||||
}
|
||||
},
|
||||
checkCalls: checkCallsDeployedComponent,
|
||||
},
|
||||
@@ -447,6 +501,17 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
||||
fsOptions{
|
||||
generated: []string{"devfile.yaml"},
|
||||
}),
|
||||
"nodeJS sources, Devfile and orphan devstate file": nodeJsSourcesAndDevfileFsContent(
|
||||
filepath.Join("source", "devfiles", "nodejs", "devfile.yaml"),
|
||||
fsOptions{
|
||||
dotOdoExists: true,
|
||||
devstateFiles: []state.Content{
|
||||
{
|
||||
PID: 43,
|
||||
Platform: commonflags.PlatformPodman,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
runningInOptions: map[string]runningInOption{
|
||||
"no": noRunningInOption,
|
||||
@@ -457,9 +522,15 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
||||
"no": noNameOptions,
|
||||
},
|
||||
|
||||
checkOutput: func(t *testing.T, s string) {
|
||||
checkOutput: func(t *testing.T, testContext testContext, s string) {
|
||||
gomega.Expect(s).To(gomega.ContainSubstring("The following resources will get deleted from cluster"))
|
||||
gomega.Expect(s).To(gomega.ContainSubstring("- Deployment: my-component-app"))
|
||||
if testContext.fscontent == "nodeJS sources, Devfile and orphan devstate file" &&
|
||||
testContext.runningInOption != "deploy" {
|
||||
gomega.Expect(s).To(gomega.ContainSubstring("devstate.43.json"))
|
||||
} else {
|
||||
gomega.Expect(s).To(gomega.Not(gomega.ContainSubstring("devstate.43.json")))
|
||||
}
|
||||
},
|
||||
checkCalls: checkCallsDeployedComponent,
|
||||
},
|
||||
@@ -514,6 +585,7 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
||||
t.Fatalf(message)
|
||||
})
|
||||
clientset := clientset.Clientset{}
|
||||
clientset.FS = filesystem.DefaultFs{}
|
||||
env := map[string]string{}
|
||||
config := map[string]string{}
|
||||
platformFunc(t, env, config, &clientset)
|
||||
@@ -535,7 +607,7 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
||||
}
|
||||
}
|
||||
if tt.checkOutput != nil {
|
||||
tt.checkOutput(t, stdout)
|
||||
tt.checkOutput(t, testCtx, stdout)
|
||||
}
|
||||
|
||||
if tt.checkFS != nil {
|
||||
|
||||
@@ -188,6 +188,30 @@ Various factors are responsible for this:
|
||||
|
||||
Please refer to [Troubleshoot Storage Permission issues on managed cloud providers clusters](./user-guides/advanced/using-odo-with-other-clusters.md) for possible solutions to fix this.
|
||||
|
||||
### Orphan Devstate files
|
||||
|
||||
An `odo dev` session creates a `.odo/devstate.<PID>.json` file when the session starts, and deletes it at the end of the session.
|
||||
|
||||
If the session terminates abrupty, the state file won't be deleted, and will remain in the `.odo` directory.
|
||||
|
||||
You can delete such orphan devstate files using the command `odo delete component`.
|
||||
|
||||
<details>
|
||||
<summary>Example output</summary>
|
||||
|
||||
```shell
|
||||
$ odo delete component
|
||||
Searching resources to delete, please wait...
|
||||
This will delete "go" from podman.
|
||||
• The following pods and associated volumes will get deleted from podman:
|
||||
• - go-app
|
||||
|
||||
This will delete the following files and directories:
|
||||
- /home/user/projects/go/.odo/devstate.83932.json
|
||||
- /home/user/projects/go/.odo/devstate.json
|
||||
```
|
||||
</details>
|
||||
|
||||
## Podman Issues
|
||||
|
||||
### `odo` says it cannot access Podman
|
||||
|
||||
1
go.mod
1
go.mod
@@ -151,6 +151,7 @@ require (
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.1 // indirect
|
||||
github.com/moby/buildkit v0.11.6 // indirect
|
||||
|
||||
2
go.sum
generated
2
go.sum
generated
@@ -882,6 +882,8 @@ github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
|
||||
@@ -310,9 +310,14 @@ func (o *ComponentOptions) deleteDevfileComponent(ctx context.Context) ([]unstru
|
||||
hasPodmanResources = len(podmanPods) != 0
|
||||
}
|
||||
|
||||
orphans, err := o.getOrphanDevstateFiles(o.clientset.FS, ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !(hasClusterResources || hasPodmanResources) {
|
||||
log.Finfof(o.clientset.Stdout, messageWithPlatforms(o.clientset.KubernetesClient != nil, o.clientset.PodmanClient != nil, componentName, namespace))
|
||||
if !o.withFilesFlag {
|
||||
if !o.withFilesFlag && len(orphans) == 0 {
|
||||
// check for resources here
|
||||
return remainingResources, nil
|
||||
}
|
||||
@@ -326,9 +331,15 @@ func (o *ComponentOptions) deleteDevfileComponent(ctx context.Context) ([]unstru
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
filesToDelete = append(filesToDelete, orphans...)
|
||||
|
||||
hasFilesToDelete := len(filesToDelete) != 0
|
||||
|
||||
if hasFilesToDelete {
|
||||
o.printFileCreatedByOdo(filesToDelete, hasClusterResources)
|
||||
}
|
||||
hasFilesToDelete := len(filesToDelete) != 0
|
||||
|
||||
if !(hasClusterResources || hasPodmanResources || hasFilesToDelete) {
|
||||
klog.V(2).Info("no cluster resources and no files to delete")
|
||||
@@ -386,7 +397,7 @@ func (o *ComponentOptions) deleteDevfileComponent(ctx context.Context) ([]unstru
|
||||
log.Finfof(o.clientset.Stdout, "The component %q is successfully deleted from podman", componentName)
|
||||
}
|
||||
|
||||
if o.withFilesFlag {
|
||||
if o.withFilesFlag || len(orphans) > 0 {
|
||||
// Delete files
|
||||
remainingFiles := o.deleteFilesCreatedByOdo(o.clientset.FS, filesToDelete)
|
||||
var listOfFiles []string
|
||||
@@ -495,6 +506,20 @@ func getFilesCreatedByOdo(filesys filesystem.Filesystem, ctx context.Context) ([
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// getOrphanDevstateFiles gets the list of all Devstate files for which no odo process exists
|
||||
func (o *ComponentOptions) getOrphanDevstateFiles(filesys filesystem.Filesystem, ctx context.Context) ([]string, error) {
|
||||
var list []string
|
||||
if o.runningIn != labels.ComponentDeployMode {
|
||||
var orphanDevstates []string
|
||||
orphanDevstates, err := o.clientset.StateClient.GetOrphanFiles(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list = append(list, orphanDevstates...)
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (o *ComponentOptions) printFileCreatedByOdo(files []string, hasClusterResources bool) {
|
||||
if len(files) == 0 {
|
||||
return
|
||||
@@ -544,7 +569,7 @@ func NewCmdComponent(ctx context.Context, name, fullName string, testClientset c
|
||||
componentCmd.Flags().BoolVarP(&o.withFilesFlag, "files", "", false, "Delete all files and directories generated by odo. Use with caution.")
|
||||
componentCmd.Flags().BoolVarP(&o.forceFlag, "force", "f", false, "Delete component without prompting")
|
||||
componentCmd.Flags().BoolVarP(&o.waitFlag, "wait", "w", false, "Wait for deletion of all dependent resources")
|
||||
clientset.Add(componentCmd, clientset.DELETE_COMPONENT, clientset.KUBERNETES, clientset.FILESYSTEM)
|
||||
clientset.Add(componentCmd, clientset.DELETE_COMPONENT, clientset.KUBERNETES, clientset.FILESYSTEM, clientset.STATE)
|
||||
if feature.IsEnabled(ctx, feature.GenericPlatformFlag) {
|
||||
clientset.Add(componentCmd, clientset.PODMAN_NULLABLE)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/devfile/library/v2/pkg/devfile/parser"
|
||||
"github.com/devfile/library/v2/pkg/testingutil/filesystem"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -23,7 +22,10 @@ import (
|
||||
odocontext "github.com/redhat-developer/odo/pkg/odo/context"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset"
|
||||
"github.com/redhat-developer/odo/pkg/podman"
|
||||
"github.com/redhat-developer/odo/pkg/state"
|
||||
"github.com/redhat-developer/odo/pkg/testingutil"
|
||||
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||
"github.com/redhat-developer/odo/pkg/testingutil/system"
|
||||
)
|
||||
|
||||
func TestComponentOptions_deleteNamedComponent(t *testing.T) {
|
||||
@@ -420,6 +422,7 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
remainingResources []unstructured.Unstructured
|
||||
wantErr bool
|
||||
deleteClient func(ctrl *gomock.Controller) _delete.Client
|
||||
stateClient func(ctrl *gomock.Controller) state.Client
|
||||
}{
|
||||
{
|
||||
name: "deleting a component with access to devfile",
|
||||
@@ -433,6 +436,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient.EXPECT().DeleteResources(resources, false).Return([]unstructured.Unstructured{})
|
||||
return deleteClient
|
||||
},
|
||||
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||
fs := filesystem.NewFakeFs()
|
||||
system := system.Default{}
|
||||
return state.NewStateClient(fs, system)
|
||||
},
|
||||
fields: fields{
|
||||
forceFlag: true,
|
||||
},
|
||||
@@ -450,6 +458,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient.EXPECT().DeleteResources(resources, false).Return([]unstructured.Unstructured{})
|
||||
return deleteClient
|
||||
},
|
||||
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||
fs := filesystem.NewFakeFs()
|
||||
system := system.Default{}
|
||||
return state.NewStateClient(fs, system)
|
||||
},
|
||||
fields: fields{
|
||||
forceFlag: true,
|
||||
runningIn: labels.ComponentDevMode,
|
||||
@@ -468,6 +481,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient.EXPECT().DeleteResources(resources, false).Return([]unstructured.Unstructured{})
|
||||
return deleteClient
|
||||
},
|
||||
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||
fs := filesystem.NewFakeFs()
|
||||
system := system.Default{}
|
||||
return state.NewStateClient(fs, system)
|
||||
},
|
||||
fields: fields{
|
||||
forceFlag: true,
|
||||
runningIn: labels.ComponentDeployMode,
|
||||
@@ -484,6 +502,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
Return(resources, nil)
|
||||
return deleteClient
|
||||
},
|
||||
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||
fs := filesystem.NewFakeFs()
|
||||
system := system.Default{}
|
||||
return state.NewStateClient(fs, system)
|
||||
},
|
||||
fields: fields{
|
||||
forceFlag: true,
|
||||
runningIn: labels.ComponentDeployMode,
|
||||
@@ -501,6 +524,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient.EXPECT().DeleteResources(resources, false).Return(nil)
|
||||
return deleteClient
|
||||
},
|
||||
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||
fs := filesystem.NewFakeFs()
|
||||
system := system.Default{}
|
||||
return state.NewStateClient(fs, system)
|
||||
},
|
||||
fields: fields{
|
||||
forceFlag: true,
|
||||
},
|
||||
@@ -513,6 +541,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient.EXPECT().ListClusterResourcesToDeleteFromDevfile(gomock.Any(), appName, gomock.Any(), labels.ComponentAnyMode).Return(false, nil, errors.New("some error"))
|
||||
return deleteClient
|
||||
},
|
||||
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||
fs := filesystem.NewFakeFs()
|
||||
system := system.Default{}
|
||||
return state.NewStateClient(fs, system)
|
||||
},
|
||||
fields: fields{
|
||||
forceFlag: true,
|
||||
},
|
||||
@@ -525,6 +558,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient.EXPECT().ListClusterResourcesToDeleteFromDevfile(gomock.Any(), appName, gomock.Any(), labels.ComponentDevMode).Return(false, nil, errors.New("some error"))
|
||||
return deleteClient
|
||||
},
|
||||
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||
fs := filesystem.NewFakeFs()
|
||||
system := system.Default{}
|
||||
return state.NewStateClient(fs, system)
|
||||
},
|
||||
fields: fields{
|
||||
forceFlag: true,
|
||||
runningIn: labels.ComponentDevMode,
|
||||
@@ -538,6 +576,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient.EXPECT().ListClusterResourcesToDeleteFromDevfile(gomock.Any(), appName, gomock.Any(), labels.ComponentDeployMode).Return(false, nil, errors.New("some error"))
|
||||
return deleteClient
|
||||
},
|
||||
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||
fs := filesystem.NewFakeFs()
|
||||
system := system.Default{}
|
||||
return state.NewStateClient(fs, system)
|
||||
},
|
||||
fields: fields{
|
||||
forceFlag: true,
|
||||
runningIn: labels.ComponentDeployMode,
|
||||
@@ -552,6 +595,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentAnyMode)
|
||||
return deleteClient
|
||||
},
|
||||
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||
fs := filesystem.NewFakeFs()
|
||||
system := system.Default{}
|
||||
return state.NewStateClient(fs, system)
|
||||
},
|
||||
fields: fields{
|
||||
forceFlag: false,
|
||||
},
|
||||
@@ -565,6 +613,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentDevMode)
|
||||
return deleteClient
|
||||
},
|
||||
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||
fs := filesystem.NewFakeFs()
|
||||
system := system.Default{}
|
||||
return state.NewStateClient(fs, system)
|
||||
},
|
||||
fields: fields{
|
||||
forceFlag: false,
|
||||
runningIn: labels.ComponentDevMode,
|
||||
@@ -579,6 +632,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentDeployMode)
|
||||
return deleteClient
|
||||
},
|
||||
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||
fs := filesystem.NewFakeFs()
|
||||
system := system.Default{}
|
||||
return state.NewStateClient(fs, system)
|
||||
},
|
||||
fields: fields{
|
||||
forceFlag: false,
|
||||
runningIn: labels.ComponentDeployMode,
|
||||
@@ -594,6 +652,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentAnyMode)
|
||||
return deleteClient
|
||||
},
|
||||
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||
fs := filesystem.NewFakeFs()
|
||||
system := system.Default{}
|
||||
return state.NewStateClient(fs, system)
|
||||
},
|
||||
fields: fields{
|
||||
forceFlag: true,
|
||||
},
|
||||
@@ -608,6 +671,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentDevMode)
|
||||
return deleteClient
|
||||
},
|
||||
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||
fs := filesystem.NewFakeFs()
|
||||
system := system.Default{}
|
||||
return state.NewStateClient(fs, system)
|
||||
},
|
||||
fields: fields{
|
||||
forceFlag: true,
|
||||
runningIn: labels.ComponentDevMode,
|
||||
@@ -623,6 +691,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentDeployMode)
|
||||
return deleteClient
|
||||
},
|
||||
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||
fs := filesystem.NewFakeFs()
|
||||
system := system.Default{}
|
||||
return state.NewStateClient(fs, system)
|
||||
},
|
||||
fields: fields{
|
||||
forceFlag: true,
|
||||
runningIn: labels.ComponentDeployMode,
|
||||
@@ -640,6 +713,7 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
kubeClient := prepareKubeClient(ctrl, projectName)
|
||||
deleteClient := tt.deleteClient(ctrl)
|
||||
stateClient := tt.stateClient(ctrl)
|
||||
o := &ComponentOptions{
|
||||
name: tt.fields.name,
|
||||
forceFlag: tt.fields.forceFlag,
|
||||
@@ -649,6 +723,7 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
Stderr: os.Stderr,
|
||||
KubernetesClient: kubeClient,
|
||||
DeleteClient: deleteClient,
|
||||
StateClient: stateClient,
|
||||
},
|
||||
}
|
||||
ctx := odocontext.WithNamespace(context.Background(), projectName)
|
||||
@@ -656,6 +731,7 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
ctx = odocontext.WithWorkingDirectory(ctx, workingDir)
|
||||
ctx = odocontext.WithComponentName(ctx, compName)
|
||||
ctx = odocontext.WithEffectiveDevfileObj(ctx, &info)
|
||||
ctx = odocontext.WithPID(ctx, 101)
|
||||
remainingResources, err := o.deleteDevfileComponent(ctx)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("deleteDevfileComponent() error = %v, wantErr %v", err, tt.wantErr)
|
||||
|
||||
@@ -44,6 +44,7 @@ import (
|
||||
"github.com/redhat-developer/odo/pkg/project"
|
||||
"github.com/redhat-developer/odo/pkg/registry"
|
||||
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||
"github.com/redhat-developer/odo/pkg/testingutil/system"
|
||||
"github.com/redhat-developer/odo/pkg/watch"
|
||||
)
|
||||
|
||||
@@ -90,6 +91,8 @@ const (
|
||||
STATE = "DEP_STATE"
|
||||
// SYNC instantiates client for pkg/sync
|
||||
SYNC = "DEP_SYNC"
|
||||
// SYSTEM instantiates client for pkg/testingutil/system
|
||||
SYSTEM = "DEP_SYSTEM"
|
||||
// WATCH instantiates client for pkg/watch
|
||||
WATCH = "DEP_WATCH"
|
||||
/* Add key for new package here */
|
||||
@@ -122,7 +125,7 @@ var subdeps map[string][]string = map[string][]string{
|
||||
PORT_FORWARD: {KUBERNETES_NULLABLE, EXEC, STATE},
|
||||
PROJECT: {KUBERNETES},
|
||||
REGISTRY: {FILESYSTEM, PREFERENCE, KUBERNETES_NULLABLE},
|
||||
STATE: {FILESYSTEM},
|
||||
STATE: {FILESYSTEM, SYSTEM},
|
||||
SYNC: {EXEC},
|
||||
WATCH: {INFORMER, KUBERNETES_NULLABLE},
|
||||
BINDING: {PROJECT, KUBERNETES_NULLABLE},
|
||||
@@ -152,6 +155,7 @@ type Clientset struct {
|
||||
RegistryClient registry.Client
|
||||
StateClient state.Client
|
||||
SyncClient sync.Client
|
||||
systemClient system.System
|
||||
WatchClient watch.Client
|
||||
/* Add client by alphabetic order */
|
||||
}
|
||||
@@ -201,6 +205,13 @@ func Fetch(command *cobra.Command, platform string, testClientset Clientset) (*C
|
||||
dep.FS = filesystem.DefaultFs{}
|
||||
}
|
||||
}
|
||||
if isDefined(command, SYSTEM) {
|
||||
if testClientset.systemClient != nil {
|
||||
dep.systemClient = testClientset.systemClient
|
||||
} else {
|
||||
dep.systemClient = system.Default{}
|
||||
}
|
||||
}
|
||||
if isDefined(command, INFORMER) {
|
||||
dep.InformerClient = informer.NewInformerClient()
|
||||
}
|
||||
@@ -289,7 +300,7 @@ func Fetch(command *cobra.Command, platform string, testClientset Clientset) (*C
|
||||
dep.ProjectClient = project.NewClient(dep.KubernetesClient)
|
||||
}
|
||||
if isDefined(command, STATE) {
|
||||
dep.StateClient = state.NewStateClient(dep.FS)
|
||||
dep.StateClient = state.NewStateClient(dep.FS, dep.systemClient)
|
||||
}
|
||||
if isDefined(command, SYNC) {
|
||||
switch platform {
|
||||
|
||||
@@ -24,4 +24,6 @@ type Client interface {
|
||||
|
||||
// GetAPIServerPorts returns the port where the API servers are listening, possibly per platform.
|
||||
GetAPIServerPorts(ctx context.Context) ([]api.DevControlPlane, error)
|
||||
|
||||
GetOrphanFiles(ctx context.Context) ([]string, error)
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func pidExists(pid int) (bool, error) {
|
||||
if pid <= 0 {
|
||||
return false, fmt.Errorf("invalid pid %v", pid)
|
||||
}
|
||||
proc, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
err = proc.Signal(syscall.Signal(0))
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if err.Error() == "os: process already finished" {
|
||||
return false, nil
|
||||
}
|
||||
errno, ok := err.(syscall.Errno)
|
||||
if !ok {
|
||||
return false, err
|
||||
}
|
||||
switch errno {
|
||||
case syscall.ESRCH:
|
||||
return false, nil
|
||||
case syscall.EPERM:
|
||||
return true, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func pidExists(pid int) (bool, error) {
|
||||
if pid <= 0 {
|
||||
return false, fmt.Errorf("invalid pid %v", pid)
|
||||
}
|
||||
_, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@@ -10,24 +10,30 @@ import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"github.com/mitchellh/go-ps"
|
||||
"k8s.io/klog"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/api"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/feature"
|
||||
"github.com/redhat-developer/odo/pkg/odo/commonflags"
|
||||
fcontext "github.com/redhat-developer/odo/pkg/odo/commonflags/context"
|
||||
odocontext "github.com/redhat-developer/odo/pkg/odo/context"
|
||||
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||
"github.com/redhat-developer/odo/pkg/testingutil/system"
|
||||
)
|
||||
|
||||
type State struct {
|
||||
content Content
|
||||
fs filesystem.Filesystem
|
||||
system system.System
|
||||
}
|
||||
|
||||
var _ Client = (*State)(nil)
|
||||
|
||||
func NewStateClient(fs filesystem.Filesystem) *State {
|
||||
func NewStateClient(fs filesystem.Filesystem, system system.System) *State {
|
||||
return &State{
|
||||
fs: fs,
|
||||
system: system,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,7 +252,7 @@ func (o *State) isFreeOrOwnedBy(pid int) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
exists, err := pidExists(savedContent.PID)
|
||||
exists, err := o.system.PidExists(savedContent.PID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -292,14 +298,99 @@ func (o *State) checkFirstInPlatform(ctx context.Context) error {
|
||||
if content.PID == pid {
|
||||
continue
|
||||
}
|
||||
exists, err := pidExists(content.PID)
|
||||
exists, err := o.system.PidExists(content.PID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
var process ps.Process
|
||||
process, err = o.system.FindProcess(content.PID)
|
||||
if err != nil {
|
||||
klog.V(4).Infof("process %d exists but is not accessible, ignoring", content.PID)
|
||||
continue
|
||||
}
|
||||
if process.Executable() != "odo" && process.Executable() != "odo.exe" {
|
||||
klog.V(4).Infof("process %d exists but is not odo, ignoring", content.PID)
|
||||
continue
|
||||
}
|
||||
// Process exists => problem
|
||||
return NewErrAlreadyRunningOnPlatform(platform, content.PID)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *State) GetOrphanFiles(ctx context.Context) ([]string, error) {
|
||||
var (
|
||||
pid = odocontext.GetPID(ctx)
|
||||
result []string
|
||||
)
|
||||
|
||||
re := regexp.MustCompile(`^devstate\.?[0-9]*\.json$`)
|
||||
entries, err := o.fs.ReadDir(_dirpath)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// No file found => no orphan files
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if !re.MatchString(entry.Name()) {
|
||||
continue
|
||||
}
|
||||
filename, err := getFullFilename(entry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jsonContent, err := o.fs.ReadFile(filepath.Join(_dirpath, entry.Name()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var content Content
|
||||
// Ignore error, to handle empty file
|
||||
_ = json.Unmarshal(jsonContent, &content)
|
||||
|
||||
if content.PID == pid {
|
||||
continue
|
||||
}
|
||||
if content.PID == 0 {
|
||||
// This is devstate.json with pid=0
|
||||
continue
|
||||
}
|
||||
exists, err := o.system.PidExists(content.PID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if exists {
|
||||
var process ps.Process
|
||||
process, err = o.system.FindProcess(content.PID)
|
||||
if err != nil {
|
||||
klog.V(4).Infof("process %d exists but is not accessible => orphan", content.PID)
|
||||
result = append(result, filename)
|
||||
continue
|
||||
}
|
||||
if process == nil {
|
||||
klog.V(4).Infof("process %d does not exist => orphan", content.PID)
|
||||
result = append(result, filename)
|
||||
continue
|
||||
}
|
||||
if process.Executable() != "odo" && process.Executable() != "odo.exe" {
|
||||
klog.V(4).Infof("process %d exists but is not odo => orphan", content.PID)
|
||||
result = append(result, filename)
|
||||
continue
|
||||
}
|
||||
// Process exists => not orphan
|
||||
klog.V(4).Infof("process %d exists and is odo => not orphan", content.PID)
|
||||
continue
|
||||
}
|
||||
klog.V(4).Infof("process %d does not exist => orphan", content.PID)
|
||||
result = append(result, filename)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func getFullFilename(entry fs.FileInfo) (string, error) {
|
||||
return filepath.Abs(filepath.Join(_dirpath, entry.Name()))
|
||||
}
|
||||
|
||||
24
pkg/testingutil/system/default.go
Normal file
24
pkg/testingutil/system/default.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package system
|
||||
|
||||
import "github.com/mitchellh/go-ps"
|
||||
|
||||
type Default struct{}
|
||||
|
||||
var _ System = Default{}
|
||||
|
||||
func (o Default) FindProcess(pid int) (ps.Process, error) {
|
||||
return ps.FindProcess(pid)
|
||||
}
|
||||
|
||||
func (o Default) PidExists(pid int) (bool, error) {
|
||||
processes, err := ps.Processes()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, process := range processes {
|
||||
if process.Pid() == pid {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
41
pkg/testingutil/system/fake.go
Normal file
41
pkg/testingutil/system/fake.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/mitchellh/go-ps"
|
||||
)
|
||||
|
||||
type Fake struct {
|
||||
ProcessId int
|
||||
ParentId int
|
||||
// PidTable is a map of pid => executable name of existing processes
|
||||
PidTable map[int]string
|
||||
}
|
||||
|
||||
func (o Fake) Pid() int {
|
||||
return o.ProcessId
|
||||
}
|
||||
|
||||
func (o Fake) PPid() int {
|
||||
return o.ParentId
|
||||
}
|
||||
|
||||
func (o Fake) Executable() string {
|
||||
return o.PidTable[o.ProcessId]
|
||||
}
|
||||
|
||||
var _ System = Fake{}
|
||||
|
||||
func (o Fake) FindProcess(pid int) (ps.Process, error) {
|
||||
if _, found := o.PidTable[pid]; found {
|
||||
o.ProcessId = pid
|
||||
return o, nil
|
||||
}
|
||||
return nil, errors.New("no process found")
|
||||
}
|
||||
|
||||
func (o Fake) PidExists(pid int) (bool, error) {
|
||||
_, found := o.PidTable[pid]
|
||||
return found, nil
|
||||
}
|
||||
8
pkg/testingutil/system/system.go
Normal file
8
pkg/testingutil/system/system.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package system
|
||||
|
||||
import "github.com/mitchellh/go-ps"
|
||||
|
||||
type System interface {
|
||||
FindProcess(pid int) (ps.Process, error)
|
||||
PidExists(pid int) (bool, error)
|
||||
}
|
||||
@@ -133,6 +133,7 @@ type DevSessionOpts struct {
|
||||
APIServerPort int
|
||||
SyncGitDir bool
|
||||
ShowLogs bool
|
||||
VerboseLevel string
|
||||
}
|
||||
|
||||
// StartDevMode starts a dev session with `odo dev`
|
||||
@@ -174,6 +175,9 @@ func StartDevMode(options DevSessionOpts) (devSession DevSession, err error) {
|
||||
if options.ShowLogs {
|
||||
args = append(args, "--logs")
|
||||
}
|
||||
if options.VerboseLevel != "" {
|
||||
args = append(args, "-v", options.VerboseLevel)
|
||||
}
|
||||
args = append(args, options.CmdlineArgs...)
|
||||
cmd := Cmd("odo", args...)
|
||||
cmd.Cmd.Stdin = c.Tty()
|
||||
@@ -220,6 +224,10 @@ func (o DevSession) Kill() {
|
||||
o.session.Kill()
|
||||
}
|
||||
|
||||
func (o DevSession) PID() int {
|
||||
return o.session.Command.Process.Pid
|
||||
}
|
||||
|
||||
// Stop a Dev session cleanly (equivalent as hitting Ctrl-c)
|
||||
func (o *DevSession) Stop() {
|
||||
if o.session == nil {
|
||||
|
||||
@@ -15,3 +15,7 @@ func terminateProc(session *gexec.Session) error {
|
||||
}
|
||||
|
||||
func setSysProcAttr(command *exec.Cmd) {}
|
||||
|
||||
func GetFirstProcess() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -35,3 +35,7 @@ func setSysProcAttr(command *exec.Cmd) {
|
||||
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
|
||||
}
|
||||
}
|
||||
|
||||
func GetFirstProcess() int {
|
||||
return 4
|
||||
}
|
||||
|
||||
@@ -590,6 +590,36 @@ ComponentSettings:
|
||||
})
|
||||
})
|
||||
|
||||
When("killing odo dev and another process replaces it", func() {
|
||||
var newDevSession helper.DevSession
|
||||
BeforeEach(func() {
|
||||
pid := devSession.PID()
|
||||
devSession.Kill()
|
||||
devSession.WaitEnd()
|
||||
devstate := fmt.Sprintf(".odo/devstate.%d.json", pid)
|
||||
newdevstate := fmt.Sprintf(".odo/devstate.%d.json", helper.GetFirstProcess())
|
||||
helper.ReplaceString(
|
||||
devstate,
|
||||
fmt.Sprintf("\"pid\": %d", pid),
|
||||
fmt.Sprintf("\"pid\": %d", helper.GetFirstProcess()))
|
||||
err := os.Rename(devstate, newdevstate)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should restart a new session successfully", func() {
|
||||
var err error
|
||||
newDevSession, err = helper.StartDevMode(helper.DevSessionOpts{
|
||||
VerboseLevel: "4",
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
logMsg := fmt.Sprintf("process %d exists but is not odo, ignoring", helper.GetFirstProcess())
|
||||
Expect(newDevSession.ErrOut).Should(ContainSubstring(logMsg))
|
||||
|
||||
newDevSession.Stop()
|
||||
newDevSession.WaitEnd()
|
||||
})
|
||||
})
|
||||
|
||||
When("stopping odo dev normally", func() {
|
||||
BeforeEach(func() {
|
||||
devSession.Stop()
|
||||
|
||||
1
vendor/github.com/mitchellh/go-ps/.gitignore
generated
vendored
Normal file
1
vendor/github.com/mitchellh/go-ps/.gitignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.vagrant/
|
||||
4
vendor/github.com/mitchellh/go-ps/.travis.yml
generated
vendored
Normal file
4
vendor/github.com/mitchellh/go-ps/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.2.1
|
||||
21
vendor/github.com/mitchellh/go-ps/LICENSE.md
generated
vendored
Normal file
21
vendor/github.com/mitchellh/go-ps/LICENSE.md
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Mitchell Hashimoto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
34
vendor/github.com/mitchellh/go-ps/README.md
generated
vendored
Normal file
34
vendor/github.com/mitchellh/go-ps/README.md
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# Process List Library for Go [](https://godoc.org/github.com/mitchellh/go-ps)
|
||||
|
||||
go-ps is a library for Go that implements OS-specific APIs to list and
|
||||
manipulate processes in a platform-safe way. The library can find and
|
||||
list processes on Linux, Mac OS X, Solaris, and Windows.
|
||||
|
||||
If you're new to Go, this library has a good amount of advanced Go educational
|
||||
value as well. It uses some advanced features of Go: build tags, accessing
|
||||
DLL methods for Windows, cgo for Darwin, etc.
|
||||
|
||||
How it works:
|
||||
|
||||
* **Darwin** uses the `sysctl` syscall to retrieve the process table.
|
||||
* **Unix** uses the procfs at `/proc` to inspect the process tree.
|
||||
* **Windows** uses the Windows API, and methods such as
|
||||
`CreateToolhelp32Snapshot` to get a point-in-time snapshot of
|
||||
the process table.
|
||||
|
||||
## Installation
|
||||
|
||||
Install using standard `go get`:
|
||||
|
||||
```
|
||||
$ go get github.com/mitchellh/go-ps
|
||||
...
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
Want to contribute? Here is a short TODO list of things that aren't
|
||||
implemented for this library that would be nice:
|
||||
|
||||
* FreeBSD support
|
||||
* Plan9 support
|
||||
43
vendor/github.com/mitchellh/go-ps/Vagrantfile
generated
vendored
Normal file
43
vendor/github.com/mitchellh/go-ps/Vagrantfile
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
|
||||
VAGRANTFILE_API_VERSION = "2"
|
||||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
config.vm.box = "chef/ubuntu-12.04"
|
||||
|
||||
config.vm.provision "shell", inline: $script
|
||||
|
||||
["vmware_fusion", "vmware_workstation"].each do |p|
|
||||
config.vm.provider "p" do |v|
|
||||
v.vmx["memsize"] = "1024"
|
||||
v.vmx["numvcpus"] = "2"
|
||||
v.vmx["cpuid.coresPerSocket"] = "1"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
$script = <<SCRIPT
|
||||
SRCROOT="/opt/go"
|
||||
|
||||
# Install Go
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential mercurial
|
||||
sudo hg clone -u release https://code.google.com/p/go ${SRCROOT}
|
||||
cd ${SRCROOT}/src
|
||||
sudo ./all.bash
|
||||
|
||||
# Setup the GOPATH
|
||||
sudo mkdir -p /opt/gopath
|
||||
cat <<EOF >/tmp/gopath.sh
|
||||
export GOPATH="/opt/gopath"
|
||||
export PATH="/opt/go/bin:\$GOPATH/bin:\$PATH"
|
||||
EOF
|
||||
sudo mv /tmp/gopath.sh /etc/profile.d/gopath.sh
|
||||
sudo chmod 0755 /etc/profile.d/gopath.sh
|
||||
|
||||
# Make sure the gopath is usable by bamboo
|
||||
sudo chown -R vagrant:vagrant $SRCROOT
|
||||
sudo chown -R vagrant:vagrant /opt/gopath
|
||||
SCRIPT
|
||||
40
vendor/github.com/mitchellh/go-ps/process.go
generated
vendored
Normal file
40
vendor/github.com/mitchellh/go-ps/process.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
// ps provides an API for finding and listing processes in a platform-agnostic
|
||||
// way.
|
||||
//
|
||||
// NOTE: If you're reading these docs online via GoDocs or some other system,
|
||||
// you might only see the Unix docs. This project makes heavy use of
|
||||
// platform-specific implementations. We recommend reading the source if you
|
||||
// are interested.
|
||||
package ps
|
||||
|
||||
// Process is the generic interface that is implemented on every platform
|
||||
// and provides common operations for processes.
|
||||
type Process interface {
|
||||
// Pid is the process ID for this process.
|
||||
Pid() int
|
||||
|
||||
// PPid is the parent process ID for this process.
|
||||
PPid() int
|
||||
|
||||
// Executable name running this process. This is not a path to the
|
||||
// executable.
|
||||
Executable() string
|
||||
}
|
||||
|
||||
// Processes returns all processes.
|
||||
//
|
||||
// This of course will be a point-in-time snapshot of when this method was
|
||||
// called. Some operating systems don't provide snapshot capability of the
|
||||
// process table, in which case the process table returned might contain
|
||||
// ephemeral entities that happened to be running when this was called.
|
||||
func Processes() ([]Process, error) {
|
||||
return processes()
|
||||
}
|
||||
|
||||
// FindProcess looks up a single process by pid.
|
||||
//
|
||||
// Process will be nil and error will be nil if a matching process is
|
||||
// not found.
|
||||
func FindProcess(pid int) (Process, error) {
|
||||
return findProcess(pid)
|
||||
}
|
||||
138
vendor/github.com/mitchellh/go-ps/process_darwin.go
generated
vendored
Normal file
138
vendor/github.com/mitchellh/go-ps/process_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
// +build darwin
|
||||
|
||||
package ps
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type DarwinProcess struct {
|
||||
pid int
|
||||
ppid int
|
||||
binary string
|
||||
}
|
||||
|
||||
func (p *DarwinProcess) Pid() int {
|
||||
return p.pid
|
||||
}
|
||||
|
||||
func (p *DarwinProcess) PPid() int {
|
||||
return p.ppid
|
||||
}
|
||||
|
||||
func (p *DarwinProcess) Executable() string {
|
||||
return p.binary
|
||||
}
|
||||
|
||||
func findProcess(pid int) (Process, error) {
|
||||
ps, err := processes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, p := range ps {
|
||||
if p.Pid() == pid {
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func processes() ([]Process, error) {
|
||||
buf, err := darwinSyscall()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
procs := make([]*kinfoProc, 0, 50)
|
||||
k := 0
|
||||
for i := _KINFO_STRUCT_SIZE; i < buf.Len(); i += _KINFO_STRUCT_SIZE {
|
||||
proc := &kinfoProc{}
|
||||
err = binary.Read(bytes.NewBuffer(buf.Bytes()[k:i]), binary.LittleEndian, proc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k = i
|
||||
procs = append(procs, proc)
|
||||
}
|
||||
|
||||
darwinProcs := make([]Process, len(procs))
|
||||
for i, p := range procs {
|
||||
darwinProcs[i] = &DarwinProcess{
|
||||
pid: int(p.Pid),
|
||||
ppid: int(p.PPid),
|
||||
binary: darwinCstring(p.Comm),
|
||||
}
|
||||
}
|
||||
|
||||
return darwinProcs, nil
|
||||
}
|
||||
|
||||
func darwinCstring(s [16]byte) string {
|
||||
i := 0
|
||||
for _, b := range s {
|
||||
if b != 0 {
|
||||
i++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return string(s[:i])
|
||||
}
|
||||
|
||||
func darwinSyscall() (*bytes.Buffer, error) {
|
||||
mib := [4]int32{_CTRL_KERN, _KERN_PROC, _KERN_PROC_ALL, 0}
|
||||
size := uintptr(0)
|
||||
|
||||
_, _, errno := syscall.Syscall6(
|
||||
syscall.SYS___SYSCTL,
|
||||
uintptr(unsafe.Pointer(&mib[0])),
|
||||
4,
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&size)),
|
||||
0,
|
||||
0)
|
||||
|
||||
if errno != 0 {
|
||||
return nil, errno
|
||||
}
|
||||
|
||||
bs := make([]byte, size)
|
||||
_, _, errno = syscall.Syscall6(
|
||||
syscall.SYS___SYSCTL,
|
||||
uintptr(unsafe.Pointer(&mib[0])),
|
||||
4,
|
||||
uintptr(unsafe.Pointer(&bs[0])),
|
||||
uintptr(unsafe.Pointer(&size)),
|
||||
0,
|
||||
0)
|
||||
|
||||
if errno != 0 {
|
||||
return nil, errno
|
||||
}
|
||||
|
||||
return bytes.NewBuffer(bs[0:size]), nil
|
||||
}
|
||||
|
||||
const (
|
||||
_CTRL_KERN = 1
|
||||
_KERN_PROC = 14
|
||||
_KERN_PROC_ALL = 0
|
||||
_KINFO_STRUCT_SIZE = 648
|
||||
)
|
||||
|
||||
type kinfoProc struct {
|
||||
_ [40]byte
|
||||
Pid int32
|
||||
_ [199]byte
|
||||
Comm [16]byte
|
||||
_ [301]byte
|
||||
PPid int32
|
||||
_ [84]byte
|
||||
}
|
||||
260
vendor/github.com/mitchellh/go-ps/process_freebsd.go
generated
vendored
Normal file
260
vendor/github.com/mitchellh/go-ps/process_freebsd.go
generated
vendored
Normal file
@@ -0,0 +1,260 @@
|
||||
// +build freebsd
|
||||
|
||||
package ps
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// copied from sys/sysctl.h
|
||||
const (
|
||||
CTL_KERN = 1 // "high kernel": proc, limits
|
||||
KERN_PROC = 14 // struct: process entries
|
||||
KERN_PROC_PID = 1 // by process id
|
||||
KERN_PROC_PROC = 8 // only return procs
|
||||
KERN_PROC_PATHNAME = 12 // path to executable
|
||||
)
|
||||
|
||||
// copied from sys/user.h
|
||||
type Kinfo_proc struct {
|
||||
Ki_structsize int32
|
||||
Ki_layout int32
|
||||
Ki_args int64
|
||||
Ki_paddr int64
|
||||
Ki_addr int64
|
||||
Ki_tracep int64
|
||||
Ki_textvp int64
|
||||
Ki_fd int64
|
||||
Ki_vmspace int64
|
||||
Ki_wchan int64
|
||||
Ki_pid int32
|
||||
Ki_ppid int32
|
||||
Ki_pgid int32
|
||||
Ki_tpgid int32
|
||||
Ki_sid int32
|
||||
Ki_tsid int32
|
||||
Ki_jobc [2]byte
|
||||
Ki_spare_short1 [2]byte
|
||||
Ki_tdev int32
|
||||
Ki_siglist [16]byte
|
||||
Ki_sigmask [16]byte
|
||||
Ki_sigignore [16]byte
|
||||
Ki_sigcatch [16]byte
|
||||
Ki_uid int32
|
||||
Ki_ruid int32
|
||||
Ki_svuid int32
|
||||
Ki_rgid int32
|
||||
Ki_svgid int32
|
||||
Ki_ngroups [2]byte
|
||||
Ki_spare_short2 [2]byte
|
||||
Ki_groups [64]byte
|
||||
Ki_size int64
|
||||
Ki_rssize int64
|
||||
Ki_swrss int64
|
||||
Ki_tsize int64
|
||||
Ki_dsize int64
|
||||
Ki_ssize int64
|
||||
Ki_xstat [2]byte
|
||||
Ki_acflag [2]byte
|
||||
Ki_pctcpu int32
|
||||
Ki_estcpu int32
|
||||
Ki_slptime int32
|
||||
Ki_swtime int32
|
||||
Ki_cow int32
|
||||
Ki_runtime int64
|
||||
Ki_start [16]byte
|
||||
Ki_childtime [16]byte
|
||||
Ki_flag int64
|
||||
Ki_kiflag int64
|
||||
Ki_traceflag int32
|
||||
Ki_stat [1]byte
|
||||
Ki_nice [1]byte
|
||||
Ki_lock [1]byte
|
||||
Ki_rqindex [1]byte
|
||||
Ki_oncpu [1]byte
|
||||
Ki_lastcpu [1]byte
|
||||
Ki_ocomm [17]byte
|
||||
Ki_wmesg [9]byte
|
||||
Ki_login [18]byte
|
||||
Ki_lockname [9]byte
|
||||
Ki_comm [20]byte
|
||||
Ki_emul [17]byte
|
||||
Ki_sparestrings [68]byte
|
||||
Ki_spareints [36]byte
|
||||
Ki_cr_flags int32
|
||||
Ki_jid int32
|
||||
Ki_numthreads int32
|
||||
Ki_tid int32
|
||||
Ki_pri int32
|
||||
Ki_rusage [144]byte
|
||||
Ki_rusage_ch [144]byte
|
||||
Ki_pcb int64
|
||||
Ki_kstack int64
|
||||
Ki_udata int64
|
||||
Ki_tdaddr int64
|
||||
Ki_spareptrs [48]byte
|
||||
Ki_spareint64s [96]byte
|
||||
Ki_sflag int64
|
||||
Ki_tdflags int64
|
||||
}
|
||||
|
||||
// UnixProcess is an implementation of Process that contains Unix-specific
|
||||
// fields and information.
|
||||
type UnixProcess struct {
|
||||
pid int
|
||||
ppid int
|
||||
state rune
|
||||
pgrp int
|
||||
sid int
|
||||
|
||||
binary string
|
||||
}
|
||||
|
||||
func (p *UnixProcess) Pid() int {
|
||||
return p.pid
|
||||
}
|
||||
|
||||
func (p *UnixProcess) PPid() int {
|
||||
return p.ppid
|
||||
}
|
||||
|
||||
func (p *UnixProcess) Executable() string {
|
||||
return p.binary
|
||||
}
|
||||
|
||||
// Refresh reloads all the data associated with this process.
|
||||
func (p *UnixProcess) Refresh() error {
|
||||
|
||||
mib := []int32{CTL_KERN, KERN_PROC, KERN_PROC_PID, int32(p.pid)}
|
||||
|
||||
buf, length, err := call_syscall(mib)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
proc_k := Kinfo_proc{}
|
||||
if length != uint64(unsafe.Sizeof(proc_k)) {
|
||||
return err
|
||||
}
|
||||
|
||||
k, err := parse_kinfo_proc(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.ppid, p.pgrp, p.sid, p.binary = copy_params(&k)
|
||||
return nil
|
||||
}
|
||||
|
||||
func copy_params(k *Kinfo_proc) (int, int, int, string) {
|
||||
n := -1
|
||||
for i, b := range k.Ki_comm {
|
||||
if b == 0 {
|
||||
break
|
||||
}
|
||||
n = i + 1
|
||||
}
|
||||
comm := string(k.Ki_comm[:n])
|
||||
|
||||
return int(k.Ki_ppid), int(k.Ki_pgid), int(k.Ki_sid), comm
|
||||
}
|
||||
|
||||
func findProcess(pid int) (Process, error) {
|
||||
mib := []int32{CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, int32(pid)}
|
||||
|
||||
_, _, err := call_syscall(mib)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newUnixProcess(pid)
|
||||
}
|
||||
|
||||
func processes() ([]Process, error) {
|
||||
results := make([]Process, 0, 50)
|
||||
|
||||
mib := []int32{CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0}
|
||||
buf, length, err := call_syscall(mib)
|
||||
if err != nil {
|
||||
return results, err
|
||||
}
|
||||
|
||||
// get kinfo_proc size
|
||||
k := Kinfo_proc{}
|
||||
procinfo_len := int(unsafe.Sizeof(k))
|
||||
count := int(length / uint64(procinfo_len))
|
||||
|
||||
// parse buf to procs
|
||||
for i := 0; i < count; i++ {
|
||||
b := buf[i*procinfo_len : i*procinfo_len+procinfo_len]
|
||||
k, err := parse_kinfo_proc(b)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
p, err := newUnixProcess(int(k.Ki_pid))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
p.ppid, p.pgrp, p.sid, p.binary = copy_params(&k)
|
||||
|
||||
results = append(results, p)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func parse_kinfo_proc(buf []byte) (Kinfo_proc, error) {
|
||||
var k Kinfo_proc
|
||||
br := bytes.NewReader(buf)
|
||||
err := binary.Read(br, binary.LittleEndian, &k)
|
||||
if err != nil {
|
||||
return k, err
|
||||
}
|
||||
|
||||
return k, nil
|
||||
}
|
||||
|
||||
func call_syscall(mib []int32) ([]byte, uint64, error) {
|
||||
miblen := uint64(len(mib))
|
||||
|
||||
// get required buffer size
|
||||
length := uint64(0)
|
||||
_, _, err := syscall.RawSyscall6(
|
||||
syscall.SYS___SYSCTL,
|
||||
uintptr(unsafe.Pointer(&mib[0])),
|
||||
uintptr(miblen),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if err != 0 {
|
||||
b := make([]byte, 0)
|
||||
return b, length, err
|
||||
}
|
||||
if length == 0 {
|
||||
b := make([]byte, 0)
|
||||
return b, length, err
|
||||
}
|
||||
// get proc info itself
|
||||
buf := make([]byte, length)
|
||||
_, _, err = syscall.RawSyscall6(
|
||||
syscall.SYS___SYSCTL,
|
||||
uintptr(unsafe.Pointer(&mib[0])),
|
||||
uintptr(miblen),
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if err != 0 {
|
||||
return buf, length, err
|
||||
}
|
||||
|
||||
return buf, length, nil
|
||||
}
|
||||
|
||||
func newUnixProcess(pid int) (*UnixProcess, error) {
|
||||
p := &UnixProcess{pid: pid}
|
||||
return p, p.Refresh()
|
||||
}
|
||||
35
vendor/github.com/mitchellh/go-ps/process_linux.go
generated
vendored
Normal file
35
vendor/github.com/mitchellh/go-ps/process_linux.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// +build linux
|
||||
|
||||
package ps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Refresh reloads all the data associated with this process.
|
||||
func (p *UnixProcess) Refresh() error {
|
||||
statPath := fmt.Sprintf("/proc/%d/stat", p.pid)
|
||||
dataBytes, err := ioutil.ReadFile(statPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// First, parse out the image name
|
||||
data := string(dataBytes)
|
||||
binStart := strings.IndexRune(data, '(') + 1
|
||||
binEnd := strings.IndexRune(data[binStart:], ')')
|
||||
p.binary = data[binStart : binStart+binEnd]
|
||||
|
||||
// Move past the image name and start parsing the rest
|
||||
data = data[binStart+binEnd+2:]
|
||||
_, err = fmt.Sscanf(data,
|
||||
"%c %d %d %d",
|
||||
&p.state,
|
||||
&p.ppid,
|
||||
&p.pgrp,
|
||||
&p.sid)
|
||||
|
||||
return err
|
||||
}
|
||||
96
vendor/github.com/mitchellh/go-ps/process_solaris.go
generated
vendored
Normal file
96
vendor/github.com/mitchellh/go-ps/process_solaris.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
// +build solaris
|
||||
|
||||
package ps
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type ushort_t uint16
|
||||
|
||||
type id_t int32
|
||||
type pid_t int32
|
||||
type uid_t int32
|
||||
type gid_t int32
|
||||
|
||||
type dev_t uint64
|
||||
type size_t uint64
|
||||
type uintptr_t uint64
|
||||
|
||||
type timestruc_t [16]byte
|
||||
|
||||
// This is copy from /usr/include/sys/procfs.h
|
||||
type psinfo_t struct {
|
||||
Pr_flag int32 /* process flags (DEPRECATED; do not use) */
|
||||
Pr_nlwp int32 /* number of active lwps in the process */
|
||||
Pr_pid pid_t /* unique process id */
|
||||
Pr_ppid pid_t /* process id of parent */
|
||||
Pr_pgid pid_t /* pid of process group leader */
|
||||
Pr_sid pid_t /* session id */
|
||||
Pr_uid uid_t /* real user id */
|
||||
Pr_euid uid_t /* effective user id */
|
||||
Pr_gid gid_t /* real group id */
|
||||
Pr_egid gid_t /* effective group id */
|
||||
Pr_addr uintptr_t /* address of process */
|
||||
Pr_size size_t /* size of process image in Kbytes */
|
||||
Pr_rssize size_t /* resident set size in Kbytes */
|
||||
Pr_pad1 size_t
|
||||
Pr_ttydev dev_t /* controlling tty device (or PRNODEV) */
|
||||
|
||||
// Guess this following 2 ushort_t values require a padding to properly
|
||||
// align to the 64bit mark.
|
||||
Pr_pctcpu ushort_t /* % of recent cpu time used by all lwps */
|
||||
Pr_pctmem ushort_t /* % of system memory used by process */
|
||||
Pr_pad64bit [4]byte
|
||||
|
||||
Pr_start timestruc_t /* process start time, from the epoch */
|
||||
Pr_time timestruc_t /* usr+sys cpu time for this process */
|
||||
Pr_ctime timestruc_t /* usr+sys cpu time for reaped children */
|
||||
Pr_fname [16]byte /* name of execed file */
|
||||
Pr_psargs [80]byte /* initial characters of arg list */
|
||||
Pr_wstat int32 /* if zombie, the wait() status */
|
||||
Pr_argc int32 /* initial argument count */
|
||||
Pr_argv uintptr_t /* address of initial argument vector */
|
||||
Pr_envp uintptr_t /* address of initial environment vector */
|
||||
Pr_dmodel [1]byte /* data model of the process */
|
||||
Pr_pad2 [3]byte
|
||||
Pr_taskid id_t /* task id */
|
||||
Pr_projid id_t /* project id */
|
||||
Pr_nzomb int32 /* number of zombie lwps in the process */
|
||||
Pr_poolid id_t /* pool id */
|
||||
Pr_zoneid id_t /* zone id */
|
||||
Pr_contract id_t /* process contract */
|
||||
Pr_filler int32 /* reserved for future use */
|
||||
Pr_lwp [128]byte /* information for representative lwp */
|
||||
}
|
||||
|
||||
func (p *UnixProcess) Refresh() error {
|
||||
var psinfo psinfo_t
|
||||
|
||||
path := fmt.Sprintf("/proc/%d/psinfo", p.pid)
|
||||
fh, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
err = binary.Read(fh, binary.LittleEndian, &psinfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.ppid = int(psinfo.Pr_ppid)
|
||||
p.binary = toString(psinfo.Pr_fname[:], 16)
|
||||
return nil
|
||||
}
|
||||
|
||||
func toString(array []byte, len int) string {
|
||||
for i := 0; i < len; i++ {
|
||||
if array[i] == 0 {
|
||||
return string(array[:i])
|
||||
}
|
||||
}
|
||||
return string(array[:])
|
||||
}
|
||||
95
vendor/github.com/mitchellh/go-ps/process_unix.go
generated
vendored
Normal file
95
vendor/github.com/mitchellh/go-ps/process_unix.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
// +build linux solaris
|
||||
|
||||
package ps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// UnixProcess is an implementation of Process that contains Unix-specific
|
||||
// fields and information.
|
||||
type UnixProcess struct {
|
||||
pid int
|
||||
ppid int
|
||||
state rune
|
||||
pgrp int
|
||||
sid int
|
||||
|
||||
binary string
|
||||
}
|
||||
|
||||
func (p *UnixProcess) Pid() int {
|
||||
return p.pid
|
||||
}
|
||||
|
||||
func (p *UnixProcess) PPid() int {
|
||||
return p.ppid
|
||||
}
|
||||
|
||||
func (p *UnixProcess) Executable() string {
|
||||
return p.binary
|
||||
}
|
||||
|
||||
func findProcess(pid int) (Process, error) {
|
||||
dir := fmt.Sprintf("/proc/%d", pid)
|
||||
_, err := os.Stat(dir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newUnixProcess(pid)
|
||||
}
|
||||
|
||||
func processes() ([]Process, error) {
|
||||
d, err := os.Open("/proc")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer d.Close()
|
||||
|
||||
results := make([]Process, 0, 50)
|
||||
for {
|
||||
names, err := d.Readdirnames(10)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
// We only care if the name starts with a numeric
|
||||
if name[0] < '0' || name[0] > '9' {
|
||||
continue
|
||||
}
|
||||
|
||||
// From this point forward, any errors we just ignore, because
|
||||
// it might simply be that the process doesn't exist anymore.
|
||||
pid, err := strconv.ParseInt(name, 10, 0)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
p, err := newUnixProcess(int(pid))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, p)
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func newUnixProcess(pid int) (*UnixProcess, error) {
|
||||
p := &UnixProcess{pid: pid}
|
||||
return p, p.Refresh()
|
||||
}
|
||||
119
vendor/github.com/mitchellh/go-ps/process_windows.go
generated
vendored
Normal file
119
vendor/github.com/mitchellh/go-ps/process_windows.go
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
// +build windows
|
||||
|
||||
package ps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Windows API functions
|
||||
var (
|
||||
modKernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
procCloseHandle = modKernel32.NewProc("CloseHandle")
|
||||
procCreateToolhelp32Snapshot = modKernel32.NewProc("CreateToolhelp32Snapshot")
|
||||
procProcess32First = modKernel32.NewProc("Process32FirstW")
|
||||
procProcess32Next = modKernel32.NewProc("Process32NextW")
|
||||
)
|
||||
|
||||
// Some constants from the Windows API
|
||||
const (
|
||||
ERROR_NO_MORE_FILES = 0x12
|
||||
MAX_PATH = 260
|
||||
)
|
||||
|
||||
// PROCESSENTRY32 is the Windows API structure that contains a process's
|
||||
// information.
|
||||
type PROCESSENTRY32 struct {
|
||||
Size uint32
|
||||
CntUsage uint32
|
||||
ProcessID uint32
|
||||
DefaultHeapID uintptr
|
||||
ModuleID uint32
|
||||
CntThreads uint32
|
||||
ParentProcessID uint32
|
||||
PriorityClassBase int32
|
||||
Flags uint32
|
||||
ExeFile [MAX_PATH]uint16
|
||||
}
|
||||
|
||||
// WindowsProcess is an implementation of Process for Windows.
|
||||
type WindowsProcess struct {
|
||||
pid int
|
||||
ppid int
|
||||
exe string
|
||||
}
|
||||
|
||||
func (p *WindowsProcess) Pid() int {
|
||||
return p.pid
|
||||
}
|
||||
|
||||
func (p *WindowsProcess) PPid() int {
|
||||
return p.ppid
|
||||
}
|
||||
|
||||
func (p *WindowsProcess) Executable() string {
|
||||
return p.exe
|
||||
}
|
||||
|
||||
func newWindowsProcess(e *PROCESSENTRY32) *WindowsProcess {
|
||||
// Find when the string ends for decoding
|
||||
end := 0
|
||||
for {
|
||||
if e.ExeFile[end] == 0 {
|
||||
break
|
||||
}
|
||||
end++
|
||||
}
|
||||
|
||||
return &WindowsProcess{
|
||||
pid: int(e.ProcessID),
|
||||
ppid: int(e.ParentProcessID),
|
||||
exe: syscall.UTF16ToString(e.ExeFile[:end]),
|
||||
}
|
||||
}
|
||||
|
||||
func findProcess(pid int) (Process, error) {
|
||||
ps, err := processes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, p := range ps {
|
||||
if p.Pid() == pid {
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func processes() ([]Process, error) {
|
||||
handle, _, _ := procCreateToolhelp32Snapshot.Call(
|
||||
0x00000002,
|
||||
0)
|
||||
if handle < 0 {
|
||||
return nil, syscall.GetLastError()
|
||||
}
|
||||
defer procCloseHandle.Call(handle)
|
||||
|
||||
var entry PROCESSENTRY32
|
||||
entry.Size = uint32(unsafe.Sizeof(entry))
|
||||
ret, _, _ := procProcess32First.Call(handle, uintptr(unsafe.Pointer(&entry)))
|
||||
if ret == 0 {
|
||||
return nil, fmt.Errorf("Error retrieving process info.")
|
||||
}
|
||||
|
||||
results := make([]Process, 0, 50)
|
||||
for {
|
||||
results = append(results, newWindowsProcess(&entry))
|
||||
|
||||
ret, _, _ := procProcess32Next.Call(handle, uintptr(unsafe.Pointer(&entry)))
|
||||
if ret == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
3
vendor/modules.txt
generated
vendored
3
vendor/modules.txt
generated
vendored
@@ -545,6 +545,9 @@ github.com/matttproud/golang_protobuf_extensions/pbutil
|
||||
# github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
|
||||
## explicit
|
||||
github.com/mgutz/ansi
|
||||
# github.com/mitchellh/go-ps v1.0.0
|
||||
## explicit; go 1.13
|
||||
github.com/mitchellh/go-ps
|
||||
# github.com/mitchellh/go-wordwrap v1.0.0
|
||||
## explicit
|
||||
github.com/mitchellh/go-wordwrap
|
||||
|
||||
Reference in New Issue
Block a user