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"
|
"github.com/redhat-developer/odo/pkg/config"
|
||||||
envcontext "github.com/redhat-developer/odo/pkg/config/context"
|
envcontext "github.com/redhat-developer/odo/pkg/config/context"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/cli"
|
"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/odo/genericclioptions/clientset"
|
||||||
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||||
)
|
)
|
||||||
@@ -67,6 +68,7 @@ func runCommand(
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
ctx = envcontext.WithEnvConfig(ctx, *envConfig)
|
ctx = envcontext.WithEnvConfig(ctx, *envConfig)
|
||||||
|
ctx = odocontext.WithPID(ctx, 101)
|
||||||
|
|
||||||
for k, v := range options.env {
|
for k, v := range options.env {
|
||||||
t.Setenv(k, v)
|
t.Setenv(k, v)
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -14,8 +16,10 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
|
||||||
"github.com/redhat-developer/odo/pkg/kclient"
|
"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/odo/genericclioptions/clientset"
|
||||||
"github.com/redhat-developer/odo/pkg/podman"
|
"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/testingutil/filesystem"
|
||||||
"github.com/redhat-developer/odo/pkg/util"
|
"github.com/redhat-developer/odo/pkg/util"
|
||||||
"github.com/redhat-developer/odo/tests/helper"
|
"github.com/redhat-developer/odo/tests/helper"
|
||||||
@@ -95,8 +99,9 @@ var nodeJsSourcesFsContent fscontentFunc = func(fs filesystem.Filesystem) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
type fsOptions struct {
|
type fsOptions struct {
|
||||||
dotOdoExists bool
|
dotOdoExists bool
|
||||||
generated []string
|
generated []string
|
||||||
|
devstateFiles []state.Content
|
||||||
}
|
}
|
||||||
|
|
||||||
var nodeJsSourcesAndDevfileFsContent = func(devfilePath string, options fsOptions) fscontentFunc {
|
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())
|
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
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,9 +270,9 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
|||||||
nameOptions map[string]nameOption
|
nameOptions map[string]nameOption
|
||||||
|
|
||||||
wantErr string
|
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)
|
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",
|
name: "delete component when Devfile is not present in the directory",
|
||||||
@@ -324,7 +344,7 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
|||||||
"no": noNameOptions,
|
"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")
|
gomega.Expect(s).ToNot(gomega.ContainSubstring("devfile.yaml"), "should not list the devfile.yaml")
|
||||||
},
|
},
|
||||||
checkFS: func(t *testing.T, fs filesystem.Filesystem) {
|
checkFS: func(t *testing.T, fs filesystem.Filesystem) {
|
||||||
@@ -353,7 +373,7 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
|||||||
"no": noNameOptions,
|
"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")
|
gomega.Expect(s).To(gomega.ContainSubstring("devfile.yaml"), "should list the devfile.yaml")
|
||||||
},
|
},
|
||||||
checkFS: func(t *testing.T, fs filesystem.Filesystem) {
|
checkFS: func(t *testing.T, fs filesystem.Filesystem) {
|
||||||
@@ -379,6 +399,17 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
|||||||
fsOptions{
|
fsOptions{
|
||||||
generated: []string{"devfile.yaml"},
|
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,
|
runningInOptions: allRunningInOptions,
|
||||||
filesOptions: allFilesOptions,
|
filesOptions: allFilesOptions,
|
||||||
@@ -386,8 +417,14 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
|||||||
"no": noNameOptions,
|
"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"))
|
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,
|
checkCalls: checkCallsNonDeployedComponent,
|
||||||
},
|
},
|
||||||
@@ -411,6 +448,17 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
|||||||
fsOptions{
|
fsOptions{
|
||||||
generated: []string{"devfile.yaml"},
|
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{
|
runningInOptions: map[string]runningInOption{
|
||||||
"no": noRunningInOption,
|
"no": noRunningInOption,
|
||||||
@@ -421,9 +469,15 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
|||||||
"no": noNameOptions,
|
"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("The following pods and associated volumes will get deleted from podman"))
|
||||||
gomega.Expect(s).To(gomega.ContainSubstring("- my-component-app"))
|
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,
|
checkCalls: checkCallsDeployedComponent,
|
||||||
},
|
},
|
||||||
@@ -447,6 +501,17 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
|||||||
fsOptions{
|
fsOptions{
|
||||||
generated: []string{"devfile.yaml"},
|
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{
|
runningInOptions: map[string]runningInOption{
|
||||||
"no": noRunningInOption,
|
"no": noRunningInOption,
|
||||||
@@ -457,9 +522,15 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
|||||||
"no": noNameOptions,
|
"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("The following resources will get deleted from cluster"))
|
||||||
gomega.Expect(s).To(gomega.ContainSubstring("- Deployment: my-component-app"))
|
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,
|
checkCalls: checkCallsDeployedComponent,
|
||||||
},
|
},
|
||||||
@@ -514,6 +585,7 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
|||||||
t.Fatalf(message)
|
t.Fatalf(message)
|
||||||
})
|
})
|
||||||
clientset := clientset.Clientset{}
|
clientset := clientset.Clientset{}
|
||||||
|
clientset.FS = filesystem.DefaultFs{}
|
||||||
env := map[string]string{}
|
env := map[string]string{}
|
||||||
config := map[string]string{}
|
config := map[string]string{}
|
||||||
platformFunc(t, env, config, &clientset)
|
platformFunc(t, env, config, &clientset)
|
||||||
@@ -535,7 +607,7 @@ func TestOdoDeleteMatrix(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if tt.checkOutput != nil {
|
if tt.checkOutput != nil {
|
||||||
tt.checkOutput(t, stdout)
|
tt.checkOutput(t, testCtx, stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tt.checkFS != nil {
|
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.
|
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
|
## Podman Issues
|
||||||
|
|
||||||
### `odo` says it cannot access Podman
|
### `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/mattn/go-runewidth v0.0.13 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // 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/go-wordwrap v1.0.0 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.1 // indirect
|
github.com/mitchellh/reflectwalk v1.0.1 // indirect
|
||||||
github.com/moby/buildkit v0.11.6 // 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/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.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-homedir v1.1.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-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 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
||||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
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
|
hasPodmanResources = len(podmanPods) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
orphans, err := o.getOrphanDevstateFiles(o.clientset.FS, ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if !(hasClusterResources || hasPodmanResources) {
|
if !(hasClusterResources || hasPodmanResources) {
|
||||||
log.Finfof(o.clientset.Stdout, messageWithPlatforms(o.clientset.KubernetesClient != nil, o.clientset.PodmanClient != nil, componentName, namespace))
|
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
|
// check for resources here
|
||||||
return remainingResources, nil
|
return remainingResources, nil
|
||||||
}
|
}
|
||||||
@@ -326,9 +331,15 @@ func (o *ComponentOptions) deleteDevfileComponent(ctx context.Context) ([]unstru
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filesToDelete = append(filesToDelete, orphans...)
|
||||||
|
|
||||||
|
hasFilesToDelete := len(filesToDelete) != 0
|
||||||
|
|
||||||
|
if hasFilesToDelete {
|
||||||
o.printFileCreatedByOdo(filesToDelete, hasClusterResources)
|
o.printFileCreatedByOdo(filesToDelete, hasClusterResources)
|
||||||
}
|
}
|
||||||
hasFilesToDelete := len(filesToDelete) != 0
|
|
||||||
|
|
||||||
if !(hasClusterResources || hasPodmanResources || hasFilesToDelete) {
|
if !(hasClusterResources || hasPodmanResources || hasFilesToDelete) {
|
||||||
klog.V(2).Info("no cluster resources and no files to delete")
|
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)
|
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
|
// Delete files
|
||||||
remainingFiles := o.deleteFilesCreatedByOdo(o.clientset.FS, filesToDelete)
|
remainingFiles := o.deleteFilesCreatedByOdo(o.clientset.FS, filesToDelete)
|
||||||
var listOfFiles []string
|
var listOfFiles []string
|
||||||
@@ -495,6 +506,20 @@ func getFilesCreatedByOdo(filesys filesystem.Filesystem, ctx context.Context) ([
|
|||||||
return list, nil
|
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) {
|
func (o *ComponentOptions) printFileCreatedByOdo(files []string, hasClusterResources bool) {
|
||||||
if len(files) == 0 {
|
if len(files) == 0 {
|
||||||
return
|
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.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.forceFlag, "force", "f", false, "Delete component without prompting")
|
||||||
componentCmd.Flags().BoolVarP(&o.waitFlag, "wait", "w", false, "Wait for deletion of all dependent resources")
|
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) {
|
if feature.IsEnabled(ctx, feature.GenericPlatformFlag) {
|
||||||
clientset.Add(componentCmd, clientset.PODMAN_NULLABLE)
|
clientset.Add(componentCmd, clientset.PODMAN_NULLABLE)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/devfile/library/v2/pkg/devfile/parser"
|
"github.com/devfile/library/v2/pkg/devfile/parser"
|
||||||
"github.com/devfile/library/v2/pkg/testingutil/filesystem"
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
@@ -23,7 +22,10 @@ import (
|
|||||||
odocontext "github.com/redhat-developer/odo/pkg/odo/context"
|
odocontext "github.com/redhat-developer/odo/pkg/odo/context"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset"
|
"github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset"
|
||||||
"github.com/redhat-developer/odo/pkg/podman"
|
"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"
|
||||||
|
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||||
|
"github.com/redhat-developer/odo/pkg/testingutil/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestComponentOptions_deleteNamedComponent(t *testing.T) {
|
func TestComponentOptions_deleteNamedComponent(t *testing.T) {
|
||||||
@@ -420,6 +422,7 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
|||||||
remainingResources []unstructured.Unstructured
|
remainingResources []unstructured.Unstructured
|
||||||
wantErr bool
|
wantErr bool
|
||||||
deleteClient func(ctrl *gomock.Controller) _delete.Client
|
deleteClient func(ctrl *gomock.Controller) _delete.Client
|
||||||
|
stateClient func(ctrl *gomock.Controller) state.Client
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "deleting a component with access to devfile",
|
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{})
|
deleteClient.EXPECT().DeleteResources(resources, false).Return([]unstructured.Unstructured{})
|
||||||
return deleteClient
|
return deleteClient
|
||||||
},
|
},
|
||||||
|
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
system := system.Default{}
|
||||||
|
return state.NewStateClient(fs, system)
|
||||||
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
forceFlag: true,
|
forceFlag: true,
|
||||||
},
|
},
|
||||||
@@ -450,6 +458,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
|||||||
deleteClient.EXPECT().DeleteResources(resources, false).Return([]unstructured.Unstructured{})
|
deleteClient.EXPECT().DeleteResources(resources, false).Return([]unstructured.Unstructured{})
|
||||||
return deleteClient
|
return deleteClient
|
||||||
},
|
},
|
||||||
|
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
system := system.Default{}
|
||||||
|
return state.NewStateClient(fs, system)
|
||||||
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
forceFlag: true,
|
forceFlag: true,
|
||||||
runningIn: labels.ComponentDevMode,
|
runningIn: labels.ComponentDevMode,
|
||||||
@@ -468,6 +481,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
|||||||
deleteClient.EXPECT().DeleteResources(resources, false).Return([]unstructured.Unstructured{})
|
deleteClient.EXPECT().DeleteResources(resources, false).Return([]unstructured.Unstructured{})
|
||||||
return deleteClient
|
return deleteClient
|
||||||
},
|
},
|
||||||
|
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
system := system.Default{}
|
||||||
|
return state.NewStateClient(fs, system)
|
||||||
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
forceFlag: true,
|
forceFlag: true,
|
||||||
runningIn: labels.ComponentDeployMode,
|
runningIn: labels.ComponentDeployMode,
|
||||||
@@ -484,6 +502,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
|||||||
Return(resources, nil)
|
Return(resources, nil)
|
||||||
return deleteClient
|
return deleteClient
|
||||||
},
|
},
|
||||||
|
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
system := system.Default{}
|
||||||
|
return state.NewStateClient(fs, system)
|
||||||
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
forceFlag: true,
|
forceFlag: true,
|
||||||
runningIn: labels.ComponentDeployMode,
|
runningIn: labels.ComponentDeployMode,
|
||||||
@@ -501,6 +524,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
|||||||
deleteClient.EXPECT().DeleteResources(resources, false).Return(nil)
|
deleteClient.EXPECT().DeleteResources(resources, false).Return(nil)
|
||||||
return deleteClient
|
return deleteClient
|
||||||
},
|
},
|
||||||
|
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
system := system.Default{}
|
||||||
|
return state.NewStateClient(fs, system)
|
||||||
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
forceFlag: true,
|
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"))
|
deleteClient.EXPECT().ListClusterResourcesToDeleteFromDevfile(gomock.Any(), appName, gomock.Any(), labels.ComponentAnyMode).Return(false, nil, errors.New("some error"))
|
||||||
return deleteClient
|
return deleteClient
|
||||||
},
|
},
|
||||||
|
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
system := system.Default{}
|
||||||
|
return state.NewStateClient(fs, system)
|
||||||
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
forceFlag: true,
|
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"))
|
deleteClient.EXPECT().ListClusterResourcesToDeleteFromDevfile(gomock.Any(), appName, gomock.Any(), labels.ComponentDevMode).Return(false, nil, errors.New("some error"))
|
||||||
return deleteClient
|
return deleteClient
|
||||||
},
|
},
|
||||||
|
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
system := system.Default{}
|
||||||
|
return state.NewStateClient(fs, system)
|
||||||
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
forceFlag: true,
|
forceFlag: true,
|
||||||
runningIn: labels.ComponentDevMode,
|
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"))
|
deleteClient.EXPECT().ListClusterResourcesToDeleteFromDevfile(gomock.Any(), appName, gomock.Any(), labels.ComponentDeployMode).Return(false, nil, errors.New("some error"))
|
||||||
return deleteClient
|
return deleteClient
|
||||||
},
|
},
|
||||||
|
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
system := system.Default{}
|
||||||
|
return state.NewStateClient(fs, system)
|
||||||
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
forceFlag: true,
|
forceFlag: true,
|
||||||
runningIn: labels.ComponentDeployMode,
|
runningIn: labels.ComponentDeployMode,
|
||||||
@@ -552,6 +595,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
|||||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentAnyMode)
|
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentAnyMode)
|
||||||
return deleteClient
|
return deleteClient
|
||||||
},
|
},
|
||||||
|
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
system := system.Default{}
|
||||||
|
return state.NewStateClient(fs, system)
|
||||||
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
forceFlag: false,
|
forceFlag: false,
|
||||||
},
|
},
|
||||||
@@ -565,6 +613,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
|||||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentDevMode)
|
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentDevMode)
|
||||||
return deleteClient
|
return deleteClient
|
||||||
},
|
},
|
||||||
|
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
system := system.Default{}
|
||||||
|
return state.NewStateClient(fs, system)
|
||||||
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
forceFlag: false,
|
forceFlag: false,
|
||||||
runningIn: labels.ComponentDevMode,
|
runningIn: labels.ComponentDevMode,
|
||||||
@@ -579,6 +632,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
|||||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentDeployMode)
|
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentDeployMode)
|
||||||
return deleteClient
|
return deleteClient
|
||||||
},
|
},
|
||||||
|
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
system := system.Default{}
|
||||||
|
return state.NewStateClient(fs, system)
|
||||||
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
forceFlag: false,
|
forceFlag: false,
|
||||||
runningIn: labels.ComponentDeployMode,
|
runningIn: labels.ComponentDeployMode,
|
||||||
@@ -594,6 +652,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
|||||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentAnyMode)
|
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentAnyMode)
|
||||||
return deleteClient
|
return deleteClient
|
||||||
},
|
},
|
||||||
|
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
system := system.Default{}
|
||||||
|
return state.NewStateClient(fs, system)
|
||||||
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
forceFlag: true,
|
forceFlag: true,
|
||||||
},
|
},
|
||||||
@@ -608,6 +671,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
|||||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentDevMode)
|
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentDevMode)
|
||||||
return deleteClient
|
return deleteClient
|
||||||
},
|
},
|
||||||
|
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
system := system.Default{}
|
||||||
|
return state.NewStateClient(fs, system)
|
||||||
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
forceFlag: true,
|
forceFlag: true,
|
||||||
runningIn: labels.ComponentDevMode,
|
runningIn: labels.ComponentDevMode,
|
||||||
@@ -623,6 +691,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
|||||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentDeployMode)
|
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentDeployMode)
|
||||||
return deleteClient
|
return deleteClient
|
||||||
},
|
},
|
||||||
|
stateClient: func(ctrl *gomock.Controller) state.Client {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
system := system.Default{}
|
||||||
|
return state.NewStateClient(fs, system)
|
||||||
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
forceFlag: true,
|
forceFlag: true,
|
||||||
runningIn: labels.ComponentDeployMode,
|
runningIn: labels.ComponentDeployMode,
|
||||||
@@ -640,6 +713,7 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
|||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
kubeClient := prepareKubeClient(ctrl, projectName)
|
kubeClient := prepareKubeClient(ctrl, projectName)
|
||||||
deleteClient := tt.deleteClient(ctrl)
|
deleteClient := tt.deleteClient(ctrl)
|
||||||
|
stateClient := tt.stateClient(ctrl)
|
||||||
o := &ComponentOptions{
|
o := &ComponentOptions{
|
||||||
name: tt.fields.name,
|
name: tt.fields.name,
|
||||||
forceFlag: tt.fields.forceFlag,
|
forceFlag: tt.fields.forceFlag,
|
||||||
@@ -649,6 +723,7 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
|||||||
Stderr: os.Stderr,
|
Stderr: os.Stderr,
|
||||||
KubernetesClient: kubeClient,
|
KubernetesClient: kubeClient,
|
||||||
DeleteClient: deleteClient,
|
DeleteClient: deleteClient,
|
||||||
|
StateClient: stateClient,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
ctx := odocontext.WithNamespace(context.Background(), projectName)
|
ctx := odocontext.WithNamespace(context.Background(), projectName)
|
||||||
@@ -656,6 +731,7 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
|||||||
ctx = odocontext.WithWorkingDirectory(ctx, workingDir)
|
ctx = odocontext.WithWorkingDirectory(ctx, workingDir)
|
||||||
ctx = odocontext.WithComponentName(ctx, compName)
|
ctx = odocontext.WithComponentName(ctx, compName)
|
||||||
ctx = odocontext.WithEffectiveDevfileObj(ctx, &info)
|
ctx = odocontext.WithEffectiveDevfileObj(ctx, &info)
|
||||||
|
ctx = odocontext.WithPID(ctx, 101)
|
||||||
remainingResources, err := o.deleteDevfileComponent(ctx)
|
remainingResources, err := o.deleteDevfileComponent(ctx)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("deleteDevfileComponent() error = %v, wantErr %v", err, 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/project"
|
||||||
"github.com/redhat-developer/odo/pkg/registry"
|
"github.com/redhat-developer/odo/pkg/registry"
|
||||||
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||||
|
"github.com/redhat-developer/odo/pkg/testingutil/system"
|
||||||
"github.com/redhat-developer/odo/pkg/watch"
|
"github.com/redhat-developer/odo/pkg/watch"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -90,6 +91,8 @@ const (
|
|||||||
STATE = "DEP_STATE"
|
STATE = "DEP_STATE"
|
||||||
// SYNC instantiates client for pkg/sync
|
// SYNC instantiates client for pkg/sync
|
||||||
SYNC = "DEP_SYNC"
|
SYNC = "DEP_SYNC"
|
||||||
|
// SYSTEM instantiates client for pkg/testingutil/system
|
||||||
|
SYSTEM = "DEP_SYSTEM"
|
||||||
// WATCH instantiates client for pkg/watch
|
// WATCH instantiates client for pkg/watch
|
||||||
WATCH = "DEP_WATCH"
|
WATCH = "DEP_WATCH"
|
||||||
/* Add key for new package here */
|
/* Add key for new package here */
|
||||||
@@ -122,7 +125,7 @@ var subdeps map[string][]string = map[string][]string{
|
|||||||
PORT_FORWARD: {KUBERNETES_NULLABLE, EXEC, STATE},
|
PORT_FORWARD: {KUBERNETES_NULLABLE, EXEC, STATE},
|
||||||
PROJECT: {KUBERNETES},
|
PROJECT: {KUBERNETES},
|
||||||
REGISTRY: {FILESYSTEM, PREFERENCE, KUBERNETES_NULLABLE},
|
REGISTRY: {FILESYSTEM, PREFERENCE, KUBERNETES_NULLABLE},
|
||||||
STATE: {FILESYSTEM},
|
STATE: {FILESYSTEM, SYSTEM},
|
||||||
SYNC: {EXEC},
|
SYNC: {EXEC},
|
||||||
WATCH: {INFORMER, KUBERNETES_NULLABLE},
|
WATCH: {INFORMER, KUBERNETES_NULLABLE},
|
||||||
BINDING: {PROJECT, KUBERNETES_NULLABLE},
|
BINDING: {PROJECT, KUBERNETES_NULLABLE},
|
||||||
@@ -152,6 +155,7 @@ type Clientset struct {
|
|||||||
RegistryClient registry.Client
|
RegistryClient registry.Client
|
||||||
StateClient state.Client
|
StateClient state.Client
|
||||||
SyncClient sync.Client
|
SyncClient sync.Client
|
||||||
|
systemClient system.System
|
||||||
WatchClient watch.Client
|
WatchClient watch.Client
|
||||||
/* Add client by alphabetic order */
|
/* Add client by alphabetic order */
|
||||||
}
|
}
|
||||||
@@ -201,6 +205,13 @@ func Fetch(command *cobra.Command, platform string, testClientset Clientset) (*C
|
|||||||
dep.FS = filesystem.DefaultFs{}
|
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) {
|
if isDefined(command, INFORMER) {
|
||||||
dep.InformerClient = informer.NewInformerClient()
|
dep.InformerClient = informer.NewInformerClient()
|
||||||
}
|
}
|
||||||
@@ -289,7 +300,7 @@ func Fetch(command *cobra.Command, platform string, testClientset Clientset) (*C
|
|||||||
dep.ProjectClient = project.NewClient(dep.KubernetesClient)
|
dep.ProjectClient = project.NewClient(dep.KubernetesClient)
|
||||||
}
|
}
|
||||||
if isDefined(command, STATE) {
|
if isDefined(command, STATE) {
|
||||||
dep.StateClient = state.NewStateClient(dep.FS)
|
dep.StateClient = state.NewStateClient(dep.FS, dep.systemClient)
|
||||||
}
|
}
|
||||||
if isDefined(command, SYNC) {
|
if isDefined(command, SYNC) {
|
||||||
switch platform {
|
switch platform {
|
||||||
|
|||||||
@@ -24,4 +24,6 @@ type Client interface {
|
|||||||
|
|
||||||
// GetAPIServerPorts returns the port where the API servers are listening, possibly per platform.
|
// GetAPIServerPorts returns the port where the API servers are listening, possibly per platform.
|
||||||
GetAPIServerPorts(ctx context.Context) ([]api.DevControlPlane, error)
|
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"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/mitchellh/go-ps"
|
||||||
|
"k8s.io/klog"
|
||||||
|
|
||||||
"github.com/redhat-developer/odo/pkg/api"
|
"github.com/redhat-developer/odo/pkg/api"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/cli/feature"
|
"github.com/redhat-developer/odo/pkg/odo/cli/feature"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/commonflags"
|
"github.com/redhat-developer/odo/pkg/odo/commonflags"
|
||||||
fcontext "github.com/redhat-developer/odo/pkg/odo/commonflags/context"
|
fcontext "github.com/redhat-developer/odo/pkg/odo/commonflags/context"
|
||||||
odocontext "github.com/redhat-developer/odo/pkg/odo/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/filesystem"
|
||||||
|
"github.com/redhat-developer/odo/pkg/testingutil/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
type State struct {
|
type State struct {
|
||||||
content Content
|
content Content
|
||||||
fs filesystem.Filesystem
|
fs filesystem.Filesystem
|
||||||
|
system system.System
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Client = (*State)(nil)
|
var _ Client = (*State)(nil)
|
||||||
|
|
||||||
func NewStateClient(fs filesystem.Filesystem) *State {
|
func NewStateClient(fs filesystem.Filesystem, system system.System) *State {
|
||||||
return &State{
|
return &State{
|
||||||
fs: fs,
|
fs: fs,
|
||||||
|
system: system,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +252,7 @@ func (o *State) isFreeOrOwnedBy(pid int) (bool, error) {
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
exists, err := pidExists(savedContent.PID)
|
exists, err := o.system.PidExists(savedContent.PID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@@ -292,14 +298,99 @@ func (o *State) checkFirstInPlatform(ctx context.Context) error {
|
|||||||
if content.PID == pid {
|
if content.PID == pid {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
exists, err := pidExists(content.PID)
|
exists, err := o.system.PidExists(content.PID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if exists {
|
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
|
// Process exists => problem
|
||||||
return NewErrAlreadyRunningOnPlatform(platform, content.PID)
|
return NewErrAlreadyRunningOnPlatform(platform, content.PID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
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
|
APIServerPort int
|
||||||
SyncGitDir bool
|
SyncGitDir bool
|
||||||
ShowLogs bool
|
ShowLogs bool
|
||||||
|
VerboseLevel string
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartDevMode starts a dev session with `odo dev`
|
// StartDevMode starts a dev session with `odo dev`
|
||||||
@@ -174,6 +175,9 @@ func StartDevMode(options DevSessionOpts) (devSession DevSession, err error) {
|
|||||||
if options.ShowLogs {
|
if options.ShowLogs {
|
||||||
args = append(args, "--logs")
|
args = append(args, "--logs")
|
||||||
}
|
}
|
||||||
|
if options.VerboseLevel != "" {
|
||||||
|
args = append(args, "-v", options.VerboseLevel)
|
||||||
|
}
|
||||||
args = append(args, options.CmdlineArgs...)
|
args = append(args, options.CmdlineArgs...)
|
||||||
cmd := Cmd("odo", args...)
|
cmd := Cmd("odo", args...)
|
||||||
cmd.Cmd.Stdin = c.Tty()
|
cmd.Cmd.Stdin = c.Tty()
|
||||||
@@ -220,6 +224,10 @@ func (o DevSession) Kill() {
|
|||||||
o.session.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)
|
// Stop a Dev session cleanly (equivalent as hitting Ctrl-c)
|
||||||
func (o *DevSession) Stop() {
|
func (o *DevSession) Stop() {
|
||||||
if o.session == nil {
|
if o.session == nil {
|
||||||
|
|||||||
@@ -15,3 +15,7 @@ func terminateProc(session *gexec.Session) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setSysProcAttr(command *exec.Cmd) {}
|
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,
|
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() {
|
When("stopping odo dev normally", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
devSession.Stop()
|
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
|
# github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
|
||||||
## explicit
|
## explicit
|
||||||
github.com/mgutz/ansi
|
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
|
# github.com/mitchellh/go-wordwrap v1.0.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/mitchellh/go-wordwrap
|
github.com/mitchellh/go-wordwrap
|
||||||
|
|||||||
Reference in New Issue
Block a user