mirror of
https://github.com/redhat-developer/odo.git
synced 2025-10-19 03:06:19 +03:00
Add odo logs (#5760)
* Add odo logs * Nolint for random number generation * Changes based on Philippe's PR review * Add logs for `odo logs` * Add nolint at the right place to fix unit tests * Changes based on PR feedback * Name the key in unstructured.Unstructured * Name containers with same names as c, c1, c2 * Remove unused struct field * Modify documentation to follow general pattern * Undo the changes done in earlier commits * odo logs help message is accurate * Update docs/website/versioned_docs/version-3.0.0/command-reference/logs.md Co-authored-by: Parthvi Vala <pvala@redhat.com> * Fixes broken link rendering * Correct the example used in odo logs doc * Make container name clearer in odo logs output * Wrap at 120 chars, not 80 * Fixes to the document after rebase mistake Co-authored-by: Parthvi Vala <pvala@redhat.com>
This commit is contained in:
@@ -0,0 +1,168 @@
|
|||||||
|
---
|
||||||
|
title: odo logs
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
`odo logs` is used to display the logs for all the containers odo created for the component under current working
|
||||||
|
directory.
|
||||||
|
|
||||||
|
## Running the command
|
||||||
|
|
||||||
|
If you haven't already done so, you must [initialize](../command-reference/init) your source code with the `odo
|
||||||
|
init` command. Next, run the `odo dev` command so that odo can create the resources on the Kubernetes cluster.
|
||||||
|
|
||||||
|
Consider a devfile.yaml like below which was used to create inner loop resources using `odo dev`. Notice that
|
||||||
|
multiple containers have been named as `main` to show how `odo logs` would display logs when more than one
|
||||||
|
containers have the same name:
|
||||||
|
```yaml
|
||||||
|
metadata:
|
||||||
|
description: Stack with Node.js 14
|
||||||
|
displayName: Node.js Runtime
|
||||||
|
icon: https://nodejs.org/static/images/logos/nodejs-new-pantone-black.svg
|
||||||
|
language: javascript
|
||||||
|
name: node
|
||||||
|
projectType: nodejs
|
||||||
|
tags:
|
||||||
|
- NodeJS
|
||||||
|
- Express
|
||||||
|
- ubi8
|
||||||
|
version: 1.0.1
|
||||||
|
schemaVersion: 2.0.0
|
||||||
|
starterProjects:
|
||||||
|
- git:
|
||||||
|
remotes:
|
||||||
|
origin: https://github.com/odo-devfiles/nodejs-ex.git
|
||||||
|
name: nodejs-starter
|
||||||
|
commands:
|
||||||
|
- exec:
|
||||||
|
commandLine: npm install
|
||||||
|
component: runtime
|
||||||
|
group:
|
||||||
|
isDefault: true
|
||||||
|
kind: build
|
||||||
|
workingDir: ${PROJECT_SOURCE}
|
||||||
|
id: install
|
||||||
|
- exec:
|
||||||
|
commandLine: npm start
|
||||||
|
component: runtime
|
||||||
|
group:
|
||||||
|
isDefault: true
|
||||||
|
kind: run
|
||||||
|
workingDir: ${PROJECT_SOURCE}
|
||||||
|
id: run
|
||||||
|
- exec:
|
||||||
|
commandLine: npm run debug
|
||||||
|
component: runtime
|
||||||
|
group:
|
||||||
|
isDefault: true
|
||||||
|
kind: debug
|
||||||
|
workingDir: ${PROJECT_SOURCE}
|
||||||
|
id: debug
|
||||||
|
- exec:
|
||||||
|
commandLine: npm test
|
||||||
|
component: runtime
|
||||||
|
group:
|
||||||
|
isDefault: true
|
||||||
|
kind: test
|
||||||
|
workingDir: ${PROJECT_SOURCE}
|
||||||
|
id: test
|
||||||
|
components:
|
||||||
|
- container:
|
||||||
|
endpoints:
|
||||||
|
- name: http-3000
|
||||||
|
targetPort: 3000
|
||||||
|
image: registry.access.redhat.com/ubi8/nodejs-14:latest
|
||||||
|
memoryLimit: 1024Mi
|
||||||
|
mountSources: true
|
||||||
|
name: runtime
|
||||||
|
- name: infinitepodone
|
||||||
|
kubernetes:
|
||||||
|
inlined: |
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: infinitepodone
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: main
|
||||||
|
image: docker.io/dharmit/infiniteloop
|
||||||
|
- name: infinitedeployment
|
||||||
|
kubernetes:
|
||||||
|
inlined: |
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: infinitedeployment
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: infinite
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: infinite
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: main
|
||||||
|
image: docker.io/dharmit/infiniteloop
|
||||||
|
```
|
||||||
|
When you do `odo dev`, odo creates pods for:
|
||||||
|
1. The component named `node` itself. Containers for this are created using `.components.container`.
|
||||||
|
2. Kubernetes component named `infinitepodone`
|
||||||
|
3. Kubernetes component named `infinitedeployment`. As can be seen under `.spec.template.spec.containers` for this
|
||||||
|
particular component, it creates one container for it.
|
||||||
|
|
||||||
|
When you run `odo logs`, you should see logs from all these containers. Each line is prefixed with
|
||||||
|
`<container-name>:` to easily distinguish which the container the logs belong to. Since we named multiple
|
||||||
|
containers in the `devfile.yaml` as `main`, `odo logs` has distinguished these containers as `main` and `main[1]`:
|
||||||
|
```shell
|
||||||
|
$ odo logs
|
||||||
|
main: Fri May 27 06:17:30 UTC 2022 - this is infinite for loop
|
||||||
|
main: Fri May 27 06:17:31 UTC 2022 - this is infinite for loop
|
||||||
|
main: Fri May 27 06:17:32 UTC 2022 - this is infinite for loop
|
||||||
|
main: Fri May 27 06:17:33 UTC 2022 - this is infinite for loop
|
||||||
|
main: Fri May 27 06:17:34 UTC 2022 - this is infinite for loop
|
||||||
|
main: Fri May 27 06:17:35 UTC 2022 - this is infinite for loop
|
||||||
|
main: Fri May 27 06:17:36 UTC 2022 - this is infinite for loop
|
||||||
|
main: Fri May 27 06:17:37 UTC 2022 - this is infinite for loop
|
||||||
|
main: Fri May 27 06:17:38 UTC 2022 - this is infinite for loop
|
||||||
|
main: Fri May 27 06:17:39 UTC 2022 - this is infinite for loop
|
||||||
|
main: Fri May 27 06:17:40 UTC 2022 - this is infinite for loop
|
||||||
|
main: Fri May 27 06:17:41 UTC 2022 - this is infinite for loop
|
||||||
|
main: Fri May 27 06:17:42 UTC 2022 - this is infinite for loop
|
||||||
|
main: Fri May 27 06:17:44 UTC 2022 - this is infinite for loop
|
||||||
|
main: Fri May 27 06:17:45 UTC 2022 - this is infinite for loop
|
||||||
|
main: Fri May 27 06:17:46 UTC 2022 - this is infinite for loop
|
||||||
|
main: Fri May 27 06:17:47 UTC 2022 - this is infinite for loop
|
||||||
|
runtime: time="2022-05-27T06:17:36Z" level=info msg="create process:devrun"
|
||||||
|
runtime: time="2022-05-27T06:17:36Z" level=info msg="create process:debugrun"
|
||||||
|
runtime: time="2022-05-27T06:17:36Z" level=info msg="try to start program" program=devrun
|
||||||
|
runtime: time="2022-05-27T06:17:36Z" level=info msg="success to start program" program=devrun
|
||||||
|
runtime: time="2022-05-27T06:17:37Z" level=debug msg="no auth required"
|
||||||
|
runtime: time="2022-05-27T06:17:37Z" level=debug msg="wait program exit" program=devrun
|
||||||
|
runtime: time="2022-05-27T06:17:37Z" level=info msg="program stopped with status:exit status 0" program=devrun
|
||||||
|
runtime: time="2022-05-27T06:17:37Z" level=info msg="Don't start the stopped program because its autorestart flag is false" program=devrun
|
||||||
|
runtime: time="2022-05-27T06:17:41Z" level=debug msg="no auth required"
|
||||||
|
runtime: time="2022-05-27T06:17:41Z" level=debug msg="succeed to find process:devrun"
|
||||||
|
runtime: time="2022-05-27T06:17:41Z" level=info msg="try to start program" program=devrun
|
||||||
|
runtime: time="2022-05-27T06:17:41Z" level=info msg="success to start program" program=devrun
|
||||||
|
runtime: ODO_COMMAND_RUN is npm start
|
||||||
|
runtime: Changing directory to ${PROJECT_SOURCE}
|
||||||
|
runtime: Executing command cd ${PROJECT_SOURCE} && npm start
|
||||||
|
runtime:
|
||||||
|
runtime: > nodejs-starter@1.0.0 start /projects
|
||||||
|
runtime: > node server.js
|
||||||
|
runtime:
|
||||||
|
runtime: App started on PORT 3000
|
||||||
|
runtime: time="2022-05-27T06:17:42Z" level=debug msg="wait program exit" program=devrun
|
||||||
|
runtime: time="2022-05-27T06:17:43Z" level=debug msg="no auth required"
|
||||||
|
main1: Fri May 27 06:17:34 UTC 2022 - this is infinite for loop
|
||||||
|
main1: Fri May 27 06:17:35 UTC 2022 - this is infinite for loop
|
||||||
|
main1: Fri May 27 06:17:36 UTC 2022 - this is infinite for loop
|
||||||
|
main1: Fri May 27 06:17:37 UTC 2022 - this is infinite for loop
|
||||||
|
main1: Fri May 27 06:17:38 UTC 2022 - this is infinite for loop
|
||||||
|
main1: Fri May 27 06:17:39 UTC 2022 - this is infinite for loop
|
||||||
|
main1: Fri May 27 06:17:40 UTC 2022 - this is infinite for loop
|
||||||
|
```
|
||||||
@@ -5,155 +5,38 @@
|
|||||||
package binding
|
package binding
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
parser "github.com/devfile/library/pkg/devfile/parser"
|
parser "github.com/devfile/library/pkg/devfile/parser"
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
api "github.com/redhat-developer/odo/pkg/api"
|
api "github.com/redhat-developer/odo/pkg/api"
|
||||||
unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
reflect "reflect"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockClient is a mock of Client interface
|
// MockClient is a mock of Client interface.
|
||||||
type MockClient struct {
|
type MockClient struct {
|
||||||
ctrl *gomock.Controller
|
ctrl *gomock.Controller
|
||||||
recorder *MockClientMockRecorder
|
recorder *MockClientMockRecorder
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockClientMockRecorder is the mock recorder for MockClient
|
// MockClientMockRecorder is the mock recorder for MockClient.
|
||||||
type MockClientMockRecorder struct {
|
type MockClientMockRecorder struct {
|
||||||
mock *MockClient
|
mock *MockClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMockClient creates a new mock instance
|
// NewMockClient creates a new mock instance.
|
||||||
func NewMockClient(ctrl *gomock.Controller) *MockClient {
|
func NewMockClient(ctrl *gomock.Controller) *MockClient {
|
||||||
mock := &MockClient{ctrl: ctrl}
|
mock := &MockClient{ctrl: ctrl}
|
||||||
mock.recorder = &MockClientMockRecorder{mock}
|
mock.recorder = &MockClientMockRecorder{mock}
|
||||||
return mock
|
return mock
|
||||||
}
|
}
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
func (m *MockClient) EXPECT() *MockClientMockRecorder {
|
func (m *MockClient) EXPECT() *MockClientMockRecorder {
|
||||||
return m.recorder
|
return m.recorder
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFlags mocks base method
|
// AddBinding mocks base method.
|
||||||
func (m *MockClient) GetFlags(flags map[string]string) map[string]string {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "GetFlags", flags)
|
|
||||||
ret0, _ := ret[0].(map[string]string)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFlags indicates an expected call of GetFlags
|
|
||||||
func (mr *MockClientMockRecorder) GetFlags(flags interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFlags", reflect.TypeOf((*MockClient)(nil).GetFlags), flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetServiceInstances mocks base method
|
|
||||||
func (m *MockClient) GetServiceInstances() (map[string]unstructured.Unstructured, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "GetServiceInstances")
|
|
||||||
ret0, _ := ret[0].(map[string]unstructured.Unstructured)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetServiceInstances indicates an expected call of GetServiceInstances
|
|
||||||
func (mr *MockClientMockRecorder) GetServiceInstances() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceInstances", reflect.TypeOf((*MockClient)(nil).GetServiceInstances))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBindingsFromDevfile mocks base method
|
|
||||||
func (m *MockClient) GetBindingsFromDevfile(devfileObj parser.DevfileObj, context string) ([]api.ServiceBinding, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "GetBindingsFromDevfile", devfileObj, context)
|
|
||||||
ret0, _ := ret[0].([]api.ServiceBinding)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBindingsFromDevfile indicates an expected call of GetBindingsFromDevfile
|
|
||||||
func (mr *MockClientMockRecorder) GetBindingsFromDevfile(devfileObj, context interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBindingsFromDevfile", reflect.TypeOf((*MockClient)(nil).GetBindingsFromDevfile), devfileObj, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBindingFromCluster mocks base method
|
|
||||||
func (m *MockClient) GetBindingFromCluster(name string) (api.ServiceBinding, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "GetBindingFromCluster", name)
|
|
||||||
ret0, _ := ret[0].(api.ServiceBinding)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBindingFromCluster indicates an expected call of GetBindingFromCluster
|
|
||||||
func (mr *MockClientMockRecorder) GetBindingFromCluster(name interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBindingFromCluster", reflect.TypeOf((*MockClient)(nil).GetBindingFromCluster), name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateAddBinding mocks base method
|
|
||||||
func (m *MockClient) ValidateAddBinding(flags map[string]string) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "ValidateAddBinding", flags)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateAddBinding indicates an expected call of ValidateAddBinding
|
|
||||||
func (mr *MockClientMockRecorder) ValidateAddBinding(flags interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateAddBinding", reflect.TypeOf((*MockClient)(nil).ValidateAddBinding), flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectServiceInstance mocks base method
|
|
||||||
func (m *MockClient) SelectServiceInstance(flags map[string]string, serviceMap map[string]unstructured.Unstructured) (string, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "SelectServiceInstance", flags, serviceMap)
|
|
||||||
ret0, _ := ret[0].(string)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectServiceInstance indicates an expected call of SelectServiceInstance
|
|
||||||
func (mr *MockClientMockRecorder) SelectServiceInstance(flags, serviceMap interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectServiceInstance", reflect.TypeOf((*MockClient)(nil).SelectServiceInstance), flags, serviceMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AskBindingName mocks base method
|
|
||||||
func (m *MockClient) AskBindingName(serviceName, componentName string, flags map[string]string) (string, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "AskBindingName", serviceName, componentName, flags)
|
|
||||||
ret0, _ := ret[0].(string)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// AskBindingName indicates an expected call of AskBindingName
|
|
||||||
func (mr *MockClientMockRecorder) AskBindingName(serviceName, componentName, flags interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AskBindingName", reflect.TypeOf((*MockClient)(nil).AskBindingName), serviceName, componentName, flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AskBindAsFiles mocks base method
|
|
||||||
func (m *MockClient) AskBindAsFiles(flags map[string]string) (bool, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "AskBindAsFiles", flags)
|
|
||||||
ret0, _ := ret[0].(bool)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// AskBindAsFiles indicates an expected call of AskBindAsFiles
|
|
||||||
func (mr *MockClientMockRecorder) AskBindAsFiles(flags interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AskBindAsFiles", reflect.TypeOf((*MockClient)(nil).AskBindAsFiles), flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddBinding mocks base method
|
|
||||||
func (m *MockClient) AddBinding(bindingName string, bindAsFiles bool, unstructuredService unstructured.Unstructured, obj parser.DevfileObj) (parser.DevfileObj, error) {
|
func (m *MockClient) AddBinding(bindingName string, bindAsFiles bool, unstructuredService unstructured.Unstructured, obj parser.DevfileObj) (parser.DevfileObj, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "AddBinding", bindingName, bindAsFiles, unstructuredService, obj)
|
ret := m.ctrl.Call(m, "AddBinding", bindingName, bindAsFiles, unstructuredService, obj)
|
||||||
@@ -162,27 +45,102 @@ func (m *MockClient) AddBinding(bindingName string, bindAsFiles bool, unstructur
|
|||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddBinding indicates an expected call of AddBinding
|
// AddBinding indicates an expected call of AddBinding.
|
||||||
func (mr *MockClientMockRecorder) AddBinding(bindingName, bindAsFiles, unstructuredService, obj interface{}) *gomock.Call {
|
func (mr *MockClientMockRecorder) AddBinding(bindingName, bindAsFiles, unstructuredService, obj interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddBinding", reflect.TypeOf((*MockClient)(nil).AddBinding), bindingName, bindAsFiles, unstructuredService, obj)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddBinding", reflect.TypeOf((*MockClient)(nil).AddBinding), bindingName, bindAsFiles, unstructuredService, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateRemoveBinding mocks base method
|
// AskBindAsFiles mocks base method.
|
||||||
func (m *MockClient) ValidateRemoveBinding(flags map[string]string) error {
|
func (m *MockClient) AskBindAsFiles(flags map[string]string) (bool, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "ValidateRemoveBinding", flags)
|
ret := m.ctrl.Call(m, "AskBindAsFiles", flags)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(bool)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// AskBindAsFiles indicates an expected call of AskBindAsFiles.
|
||||||
|
func (mr *MockClientMockRecorder) AskBindAsFiles(flags interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AskBindAsFiles", reflect.TypeOf((*MockClient)(nil).AskBindAsFiles), flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AskBindingName mocks base method.
|
||||||
|
func (m *MockClient) AskBindingName(serviceName, componentName string, flags map[string]string) (string, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "AskBindingName", serviceName, componentName, flags)
|
||||||
|
ret0, _ := ret[0].(string)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// AskBindingName indicates an expected call of AskBindingName.
|
||||||
|
func (mr *MockClientMockRecorder) AskBindingName(serviceName, componentName, flags interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AskBindingName", reflect.TypeOf((*MockClient)(nil).AskBindingName), serviceName, componentName, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBindingFromCluster mocks base method.
|
||||||
|
func (m *MockClient) GetBindingFromCluster(name string) (api.ServiceBinding, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetBindingFromCluster", name)
|
||||||
|
ret0, _ := ret[0].(api.ServiceBinding)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBindingFromCluster indicates an expected call of GetBindingFromCluster.
|
||||||
|
func (mr *MockClientMockRecorder) GetBindingFromCluster(name interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBindingFromCluster", reflect.TypeOf((*MockClient)(nil).GetBindingFromCluster), name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBindingsFromDevfile mocks base method.
|
||||||
|
func (m *MockClient) GetBindingsFromDevfile(devfileObj parser.DevfileObj, context string) ([]api.ServiceBinding, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetBindingsFromDevfile", devfileObj, context)
|
||||||
|
ret0, _ := ret[0].([]api.ServiceBinding)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBindingsFromDevfile indicates an expected call of GetBindingsFromDevfile.
|
||||||
|
func (mr *MockClientMockRecorder) GetBindingsFromDevfile(devfileObj, context interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBindingsFromDevfile", reflect.TypeOf((*MockClient)(nil).GetBindingsFromDevfile), devfileObj, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFlags mocks base method.
|
||||||
|
func (m *MockClient) GetFlags(flags map[string]string) map[string]string {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetFlags", flags)
|
||||||
|
ret0, _ := ret[0].(map[string]string)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateRemoveBinding indicates an expected call of ValidateRemoveBinding
|
// GetFlags indicates an expected call of GetFlags.
|
||||||
func (mr *MockClientMockRecorder) ValidateRemoveBinding(flags interface{}) *gomock.Call {
|
func (mr *MockClientMockRecorder) GetFlags(flags interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateRemoveBinding", reflect.TypeOf((*MockClient)(nil).ValidateRemoveBinding), flags)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFlags", reflect.TypeOf((*MockClient)(nil).GetFlags), flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveBinding mocks base method
|
// GetServiceInstances mocks base method.
|
||||||
|
func (m *MockClient) GetServiceInstances() (map[string]unstructured.Unstructured, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetServiceInstances")
|
||||||
|
ret0, _ := ret[0].(map[string]unstructured.Unstructured)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetServiceInstances indicates an expected call of GetServiceInstances.
|
||||||
|
func (mr *MockClientMockRecorder) GetServiceInstances() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceInstances", reflect.TypeOf((*MockClient)(nil).GetServiceInstances))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveBinding mocks base method.
|
||||||
func (m *MockClient) RemoveBinding(bindingName string, obj parser.DevfileObj) (parser.DevfileObj, error) {
|
func (m *MockClient) RemoveBinding(bindingName string, obj parser.DevfileObj) (parser.DevfileObj, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "RemoveBinding", bindingName, obj)
|
ret := m.ctrl.Call(m, "RemoveBinding", bindingName, obj)
|
||||||
@@ -191,8 +149,51 @@ func (m *MockClient) RemoveBinding(bindingName string, obj parser.DevfileObj) (p
|
|||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveBinding indicates an expected call of RemoveBinding
|
// RemoveBinding indicates an expected call of RemoveBinding.
|
||||||
func (mr *MockClientMockRecorder) RemoveBinding(bindingName, obj interface{}) *gomock.Call {
|
func (mr *MockClientMockRecorder) RemoveBinding(bindingName, obj interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveBinding", reflect.TypeOf((*MockClient)(nil).RemoveBinding), bindingName, obj)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveBinding", reflect.TypeOf((*MockClient)(nil).RemoveBinding), bindingName, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SelectServiceInstance mocks base method.
|
||||||
|
func (m *MockClient) SelectServiceInstance(flags map[string]string, serviceMap map[string]unstructured.Unstructured) (string, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SelectServiceInstance", flags, serviceMap)
|
||||||
|
ret0, _ := ret[0].(string)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectServiceInstance indicates an expected call of SelectServiceInstance.
|
||||||
|
func (mr *MockClientMockRecorder) SelectServiceInstance(flags, serviceMap interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectServiceInstance", reflect.TypeOf((*MockClient)(nil).SelectServiceInstance), flags, serviceMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateAddBinding mocks base method.
|
||||||
|
func (m *MockClient) ValidateAddBinding(flags map[string]string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ValidateAddBinding", flags)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateAddBinding indicates an expected call of ValidateAddBinding.
|
||||||
|
func (mr *MockClientMockRecorder) ValidateAddBinding(flags interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateAddBinding", reflect.TypeOf((*MockClient)(nil).ValidateAddBinding), flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateRemoveBinding mocks base method.
|
||||||
|
func (m *MockClient) ValidateRemoveBinding(flags map[string]string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ValidateRemoveBinding", flags)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateRemoveBinding indicates an expected call of ValidateRemoveBinding.
|
||||||
|
func (mr *MockClientMockRecorder) ValidateRemoveBinding(flags interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateRemoveBinding", reflect.TypeOf((*MockClient)(nil).ValidateRemoveBinding), flags)
|
||||||
|
}
|
||||||
|
|||||||
@@ -124,9 +124,6 @@ func Log(client kclient.ClientInterface, componentName string, appName string, f
|
|||||||
// We then return a list of "components" intended for listing / output purposes specifically for commands such as:
|
// We then return a list of "components" intended for listing / output purposes specifically for commands such as:
|
||||||
// `odo list`
|
// `odo list`
|
||||||
// that are both odo and non-odo components.
|
// that are both odo and non-odo components.
|
||||||
//
|
|
||||||
// We then return a list of "components" intended for listing / output purposes specifically for commands such as:
|
|
||||||
// `odo list`
|
|
||||||
func ListAllClusterComponents(client kclient.ClientInterface, namespace string) ([]api.ComponentAbstract, error) {
|
func ListAllClusterComponents(client kclient.ClientInterface, namespace string) ([]api.ComponentAbstract, error) {
|
||||||
|
|
||||||
// Get all the dynamic resources available
|
// Get all the dynamic resources available
|
||||||
@@ -155,8 +152,8 @@ func ListAllClusterComponents(client kclient.ClientInterface, namespace string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Figure out the correct name to use
|
// Figure out the correct name to use
|
||||||
// if there is no instance label, we SKIP the resource as
|
// if there is no instance label (app.kubernetes.io/instance),
|
||||||
// it is not a component essential for Kubernetes.
|
// we SKIP the resource as it is not a component essential for Kubernetes.
|
||||||
name := odolabels.GetComponentName(labels)
|
name := odolabels.GetComponentName(labels)
|
||||||
if name == "" {
|
if name == "" {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func (c *Client) GetBindableKinds() (bindingApi.BindableKinds, error) {
|
|||||||
return bindableKind, nil
|
return bindableKind, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBindableKindStatusRestMapping retuns a list of *meta.RESTMapping of all the bindable kind operator CRD
|
// GetBindableKindStatusRestMapping returns a list of *meta.RESTMapping of all the bindable kind operator CRD
|
||||||
func (c Client) GetBindableKindStatusRestMapping(bindableKindStatuses []bindingApi.BindableKindsStatus) ([]*meta.RESTMapping, error) {
|
func (c Client) GetBindableKindStatusRestMapping(bindableKindStatuses []bindingApi.BindableKindsStatus) ([]*meta.RESTMapping, error) {
|
||||||
var result []*meta.RESTMapping
|
var result []*meta.RESTMapping
|
||||||
for _, bks := range bindableKindStatuses {
|
for _, bks := range bindableKindStatuses {
|
||||||
|
|||||||
@@ -29,6 +29,14 @@ type ClientInterface interface {
|
|||||||
// GetAllResourcesFromSelector returns all resources of any kind (including CRs) matching the given label selector
|
// GetAllResourcesFromSelector returns all resources of any kind (including CRs) matching the given label selector
|
||||||
GetAllResourcesFromSelector(selector string, ns string) ([]unstructured.Unstructured, error)
|
GetAllResourcesFromSelector(selector string, ns string) ([]unstructured.Unstructured, error)
|
||||||
|
|
||||||
|
// binding.go
|
||||||
|
IsServiceBindingSupported() (bool, error)
|
||||||
|
GetBindableKinds() (bindingApi.BindableKinds, error)
|
||||||
|
GetBindableKindStatusRestMapping(bindableKindStatuses []bindingApi.BindableKindsStatus) ([]*meta.RESTMapping, error)
|
||||||
|
GetBindingServiceBinding(name string) (bindingApi.ServiceBinding, error)
|
||||||
|
GetSpecServiceBinding(name string) (specApi.ServiceBinding, error)
|
||||||
|
NewServiceBindingServiceObject(unstructuredService unstructured.Unstructured, bindingName string) (bindingApi.Service, error)
|
||||||
|
|
||||||
// deployment.go
|
// deployment.go
|
||||||
GetDeploymentByName(name string) (*appsv1.Deployment, error)
|
GetDeploymentByName(name string) (*appsv1.Deployment, error)
|
||||||
GetOneDeployment(componentName, appName string) (*appsv1.Deployment, error)
|
GetOneDeployment(componentName, appName string) (*appsv1.Deployment, error)
|
||||||
@@ -87,14 +95,6 @@ type ClientInterface interface {
|
|||||||
GetOperatorGVRList() ([]meta.RESTMapping, error)
|
GetOperatorGVRList() ([]meta.RESTMapping, error)
|
||||||
ConvertUnstructuredToResource(u unstructured.Unstructured, obj interface{}) error
|
ConvertUnstructuredToResource(u unstructured.Unstructured, obj interface{}) error
|
||||||
|
|
||||||
// binding.go
|
|
||||||
IsServiceBindingSupported() (bool, error)
|
|
||||||
GetBindableKinds() (bindingApi.BindableKinds, error)
|
|
||||||
GetBindableKindStatusRestMapping(bindableKindStatuses []bindingApi.BindableKindsStatus) ([]*meta.RESTMapping, error)
|
|
||||||
GetBindingServiceBinding(name string) (bindingApi.ServiceBinding, error)
|
|
||||||
GetSpecServiceBinding(name string) (specApi.ServiceBinding, error)
|
|
||||||
NewServiceBindingServiceObject(unstructuredService unstructured.Unstructured, bindingName string) (bindingApi.Service, error)
|
|
||||||
|
|
||||||
// owner_reference.go
|
// owner_reference.go
|
||||||
TryWithBlockOwnerDeletion(ownerReference metav1.OwnerReference, exec func(ownerReference metav1.OwnerReference) error) error
|
TryWithBlockOwnerDeletion(ownerReference metav1.OwnerReference, exec func(ownerReference metav1.OwnerReference) error) error
|
||||||
|
|
||||||
@@ -105,6 +105,7 @@ type ClientInterface interface {
|
|||||||
GetPodUsingComponentName(componentName string) (*corev1.Pod, error)
|
GetPodUsingComponentName(componentName string) (*corev1.Pod, error)
|
||||||
GetOnePodFromSelector(selector string) (*corev1.Pod, error)
|
GetOnePodFromSelector(selector string) (*corev1.Pod, error)
|
||||||
GetPodLogs(podName, containerName string, followLog bool) (io.ReadCloser, error)
|
GetPodLogs(podName, containerName string, followLog bool) (io.ReadCloser, error)
|
||||||
|
GetAllPodsInNamespace() (*corev1.PodList, error)
|
||||||
|
|
||||||
// port_forwarding.go
|
// port_forwarding.go
|
||||||
// SetupPortForwarding creates port-forwarding for the pod on the port pairs provided in the
|
// SetupPortForwarding creates port-forwarding for the pod on the port pairs provided in the
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -242,3 +242,7 @@ func (c *Client) GetPodLogs(podName, containerName string, followLog bool) (io.R
|
|||||||
|
|
||||||
return rd, err
|
return rd, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetAllPodsInNamespace() (*corev1.PodList, error) {
|
||||||
|
return c.KubeClient.CoreV1().Pods(c.Namespace).List(context.TODO(), metav1.ListOptions{})
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ const suffixSpacing = " "
|
|||||||
const prefixSpacing = " "
|
const prefixSpacing = " "
|
||||||
|
|
||||||
var mu sync.Mutex
|
var mu sync.Mutex
|
||||||
|
var colors = []color.Attribute{color.FgRed, color.FgGreen, color.FgYellow, color.FgBlue, color.FgMagenta, color.FgCyan, color.FgWhite}
|
||||||
|
var colorCounter = 0
|
||||||
|
|
||||||
// Status is used to track ongoing status in a CLI, with a nice loading spinner
|
// Status is used to track ongoing status in a CLI, with a nice loading spinner
|
||||||
// when attached to a terminal
|
// when attached to a terminal
|
||||||
@@ -517,3 +519,10 @@ func getSpacingString() string {
|
|||||||
}
|
}
|
||||||
return "•"
|
return "•"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ColorPicker picks a color from colors slice defined at the starting of this file
|
||||||
|
// It increments the colorCounter variable so that next iteration returns a different color
|
||||||
|
func ColorPicker() color.Attribute {
|
||||||
|
colorCounter++
|
||||||
|
return colors[(colorCounter)%len(colors)]
|
||||||
|
}
|
||||||
|
|||||||
9
pkg/logs/interface.go
Normal file
9
pkg/logs/interface.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package logs
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
type Client interface {
|
||||||
|
// DevModeLogs gets logs for the provided component name and namespace. A component could have multiple pods and
|
||||||
|
// containers running on the cluster. It returns a slice of maps where container name is the key and its logs are the value
|
||||||
|
DevModeLogs(componentName string, namespace string) ([]map[string]io.ReadCloser, error)
|
||||||
|
}
|
||||||
108
pkg/logs/logs.go
Normal file
108
pkg/logs/logs.go
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package logs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
|
||||||
|
"github.com/redhat-developer/odo/pkg/kclient"
|
||||||
|
odolabels "github.com/redhat-developer/odo/pkg/labels"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LogsClient struct {
|
||||||
|
kubernetesClient kclient.ClientInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLogsClient(kubernetesClient kclient.ClientInterface) *LogsClient {
|
||||||
|
return &LogsClient{
|
||||||
|
kubernetesClient: kubernetesClient,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Client = (*LogsClient)(nil)
|
||||||
|
|
||||||
|
func (o *LogsClient) DevModeLogs(componentName string, namespace string) ([]map[string]io.ReadCloser, error) {
|
||||||
|
// get all resources in the namespace which are running in Dev mode
|
||||||
|
selector := odolabels.Builder().WithComponentName(componentName).WithMode(odolabels.ComponentDevMode).Selector()
|
||||||
|
resources, err := o.kubernetesClient.GetAllResourcesFromSelector(selector, namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all pods in the namespace
|
||||||
|
podList, err := o.kubernetesClient.GetAllPodsInNamespace()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// match pod ownerReference (if any) with resources running in Dev mode
|
||||||
|
var pods []corev1.Pod
|
||||||
|
for _, pod := range podList.Items {
|
||||||
|
for _, owner := range pod.GetOwnerReferences() {
|
||||||
|
match, err := o.matchOwnerReferenceWithResources(owner, resources)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if match {
|
||||||
|
pods = append(pods, pod)
|
||||||
|
break // because we don't need to check other owner references of the pod anymore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all containers from the pods of interest
|
||||||
|
podContainersMap := map[string][]corev1.Container{}
|
||||||
|
for _, pod := range pods {
|
||||||
|
for _, container := range pod.Spec.Containers {
|
||||||
|
if _, ok := podContainersMap[pod.Name]; !ok {
|
||||||
|
podContainersMap[pod.Name] = []corev1.Container{container}
|
||||||
|
} else {
|
||||||
|
podContainersMap[pod.Name] = append(podContainersMap[pod.Name], container)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get logs of all containers
|
||||||
|
logs := []map[string]io.ReadCloser{}
|
||||||
|
|
||||||
|
for pod, containers := range podContainersMap {
|
||||||
|
for _, container := range containers {
|
||||||
|
containerLogs, err := o.kubernetesClient.GetPodLogs(pod, container.Name, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
logs = append(logs, map[string]io.ReadCloser{container.Name: containerLogs})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return logs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchOwnerReferenceWithResources recursively checks if the owner reference passed to it matches any of the resources
|
||||||
|
// This is useful when trying to find if a pod is owned by any of the ReplicaSet or Deployment in the cluster.
|
||||||
|
func (o *LogsClient) matchOwnerReferenceWithResources(owner metav1.OwnerReference, resources []unstructured.Unstructured) (bool, error) {
|
||||||
|
// first, check if ownerReference belongs to any of the resources
|
||||||
|
for _, resource := range resources {
|
||||||
|
if resource.GetUID() != "" && owner.UID != "" && resource.GetUID() == owner.UID {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// second, get the resource indicated by ownerReference and check its ownerReferences field
|
||||||
|
restMapping, err := o.kubernetesClient.GetRestMappingFromGVK(schema.FromAPIVersionAndKind(owner.APIVersion, owner.Kind))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
resource, err := o.kubernetesClient.GetDynamicResource(restMapping.Resource, owner.Name)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
ownerReferences := resource.GetOwnerReferences()
|
||||||
|
// recursively check if ownerReference matches any of the resources' UID
|
||||||
|
for _, ownerReference := range ownerReferences {
|
||||||
|
return o.matchOwnerReferenceWithResources(ownerReference, resources)
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
205
pkg/logs/logs_test.go
Normal file
205
pkg/logs/logs_test.go
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
package logs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
|
||||||
|
"github.com/redhat-developer/odo/pkg/kclient"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
)
|
||||||
|
|
||||||
|
func fakePod(name string) unstructured.Unstructured {
|
||||||
|
return unstructured.Unstructured{Object: map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Pod",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": fmt.Sprintf("pod-%s", name),
|
||||||
|
"uid": fmt.Sprintf("pod-%s", name),
|
||||||
|
},
|
||||||
|
"spec": map[string]interface{}{
|
||||||
|
"containers": map[string]interface{}{
|
||||||
|
"name": fmt.Sprintf("%s-1", name),
|
||||||
|
"image": "image",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fakeDeployment(name string) unstructured.Unstructured {
|
||||||
|
return unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": fmt.Sprintf("deployment-%s", name),
|
||||||
|
"uid": fmt.Sprintf("deployment-%s", name),
|
||||||
|
},
|
||||||
|
"spec": map[string]interface{}{
|
||||||
|
"selector": map[string]interface{}{
|
||||||
|
"matchLabels": map[string]interface{}{
|
||||||
|
"app": "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"template": map[string]interface{}{
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"labels": map[string]interface{}{
|
||||||
|
"app": "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"spec": fakePod(name),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateOwnerRefernce(object unstructured.Unstructured) metav1.OwnerReference {
|
||||||
|
return metav1.OwnerReference{
|
||||||
|
APIVersion: object.GetAPIVersion(),
|
||||||
|
Kind: object.GetKind(),
|
||||||
|
Name: object.GetName(),
|
||||||
|
UID: object.GetUID(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogsClient_matchOwnerReferenceWithResources_PodsWithOwnerInResources(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
resources func() []unstructured.Unstructured
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want bool
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Case 1: pod owned by a deployment",
|
||||||
|
args: args{
|
||||||
|
resources: func() []unstructured.Unstructured {
|
||||||
|
pod := fakePod("pod")
|
||||||
|
deployment := fakeDeployment("deployment")
|
||||||
|
deployOwnerRef := generateOwnerRefernce(deployment)
|
||||||
|
pod.SetOwnerReferences([]metav1.OwnerReference{deployOwnerRef})
|
||||||
|
return []unstructured.Unstructured{pod, deployment}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
kubernetesClient := kclient.NewMockClientInterface(ctrl)
|
||||||
|
o := &LogsClient{
|
||||||
|
kubernetesClient: kubernetesClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
got, err := o.matchOwnerReferenceWithResources(tt.args.resources()[0].GetOwnerReferences()[0], tt.args.resources())
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("matchOwnerReferenceWithResources() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("matchOwnerReferenceWithResources() got = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogsClient_matchOwnerReferenceWithResources_PodsWithNoOwnerInResources(t *testing.T) {
|
||||||
|
// pod and deployment that are not a part of args.resources
|
||||||
|
independentDeploy := fakeDeployment("independent-deploy")
|
||||||
|
independentPod := fakePod("independent-pod")
|
||||||
|
independentPod.SetOwnerReferences([]metav1.OwnerReference{generateOwnerRefernce(independentDeploy)})
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
resources func() []unstructured.Unstructured
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
gvk *meta.RESTMapping
|
||||||
|
resource *unstructured.Unstructured
|
||||||
|
want bool
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Case 1: Pod not owned by anything in `resources` slice",
|
||||||
|
args: args{
|
||||||
|
resources: func() []unstructured.Unstructured {
|
||||||
|
pod := fakePod("pod")
|
||||||
|
deployment := fakeDeployment("deployment")
|
||||||
|
deployOwnerRef := generateOwnerRefernce(deployment)
|
||||||
|
pod.SetOwnerReferences([]metav1.OwnerReference{deployOwnerRef})
|
||||||
|
return []unstructured.Unstructured{pod, deployment}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gvk: &meta.RESTMapping{
|
||||||
|
Resource: schema.GroupVersionResource{
|
||||||
|
Group: "apps",
|
||||||
|
Version: "v1",
|
||||||
|
Resource: "deployments",
|
||||||
|
},
|
||||||
|
GroupVersionKind: schema.GroupVersionKind{
|
||||||
|
Group: "apps",
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "Deployment",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resource: &unstructured.Unstructured{Object: map[string]interface{}{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"namespace": independentDeploy.GetNamespace(),
|
||||||
|
"name": independentDeploy.GetName(),
|
||||||
|
"uid": independentDeploy.GetUID(),
|
||||||
|
},
|
||||||
|
"spec": map[string]interface{}{
|
||||||
|
"selector": map[string]interface{}{
|
||||||
|
"matchLabels": map[string]interface{}{
|
||||||
|
"app": "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"template": map[string]interface{}{
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"labels": map[string]interface{}{
|
||||||
|
"app": "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"spec": fakePod(independentDeploy.GetName()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
want: false,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
kubernetesClient := kclient.NewMockClientInterface(ctrl)
|
||||||
|
kubernetesClient.EXPECT().GetRestMappingFromGVK(
|
||||||
|
schema.FromAPIVersionAndKind(independentDeploy.GetAPIVersion(), independentDeploy.GetKind())).Return(tt.gvk, nil).AnyTimes()
|
||||||
|
kubernetesClient.EXPECT().GetDynamicResource(tt.gvk.Resource, independentDeploy.GetName()).Return(tt.resource, nil).AnyTimes()
|
||||||
|
|
||||||
|
o := &LogsClient{
|
||||||
|
kubernetesClient: kubernetesClient,
|
||||||
|
}
|
||||||
|
got, err := o.matchOwnerReferenceWithResources(independentPod.GetOwnerReferences()[0], tt.args.resources())
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("matchOwnerReferenceWithResources() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("matchOwnerReferenceWithResources() got = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/redhat-developer/odo/pkg/odo/cli/logs"
|
||||||
|
|
||||||
"github.com/redhat-developer/odo/pkg/odo/cli/add"
|
"github.com/redhat-developer/odo/pkg/odo/cli/add"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/cli/alizer"
|
"github.com/redhat-developer/odo/pkg/odo/cli/alizer"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/cli/build_images"
|
"github.com/redhat-developer/odo/pkg/odo/cli/build_images"
|
||||||
@@ -182,6 +184,7 @@ func odoRootCmd(name, fullName string) *cobra.Command {
|
|||||||
registry.NewCmdRegistry(registry.RecommendedCommandName, util.GetFullName(fullName, registry.RecommendedCommandName)),
|
registry.NewCmdRegistry(registry.RecommendedCommandName, util.GetFullName(fullName, registry.RecommendedCommandName)),
|
||||||
create.NewCmdCreate(create.RecommendedCommandName, util.GetFullName(fullName, create.RecommendedCommandName)),
|
create.NewCmdCreate(create.RecommendedCommandName, util.GetFullName(fullName, create.RecommendedCommandName)),
|
||||||
set.NewCmdSet(set.RecommendedCommandName, util.GetFullName(fullName, set.RecommendedCommandName)),
|
set.NewCmdSet(set.RecommendedCommandName, util.GetFullName(fullName, set.RecommendedCommandName)),
|
||||||
|
logs.NewCmdLogs(logs.RecommendedCommandName, util.GetFullName(fullName, logs.RecommendedCommandName)),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Add all subcommands to base commands
|
// Add all subcommands to base commands
|
||||||
|
|||||||
158
pkg/odo/cli/logs/logs.go
Normal file
158
pkg/odo/cli/logs/logs.go
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
package logs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
|
||||||
|
"github.com/redhat-developer/odo/pkg/log"
|
||||||
|
|
||||||
|
"github.com/redhat-developer/odo/pkg/devfile/location"
|
||||||
|
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||||
|
|
||||||
|
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||||
|
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||||
|
"github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||||
|
)
|
||||||
|
|
||||||
|
const RecommendedCommandName = "logs"
|
||||||
|
|
||||||
|
type LogsOptions struct {
|
||||||
|
// context
|
||||||
|
Context *genericclioptions.Context
|
||||||
|
// clients
|
||||||
|
clientset *clientset.Clientset
|
||||||
|
|
||||||
|
// variables
|
||||||
|
componentName string
|
||||||
|
contextDir string
|
||||||
|
out io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLogsOptions() *LogsOptions {
|
||||||
|
return &LogsOptions{
|
||||||
|
out: log.GetStdout(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var logsExample = ktemplates.Examples(`
|
||||||
|
# Show logs of all containers
|
||||||
|
%[1]s
|
||||||
|
`)
|
||||||
|
|
||||||
|
func (o *LogsOptions) SetClientset(clientset *clientset.Clientset) {
|
||||||
|
o.clientset = clientset
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *LogsOptions) Complete(cmdline cmdline.Cmdline, args []string) error {
|
||||||
|
var err error
|
||||||
|
o.contextDir, err = os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
isEmptyDir, err := location.DirIsEmpty(o.clientset.FS, o.contextDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if isEmptyDir {
|
||||||
|
return errors.New("this command cannot run in an empty directory, run the command in a directory containing source code or initialize using 'odo init'")
|
||||||
|
}
|
||||||
|
|
||||||
|
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(""))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create context: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
o.componentName = o.Context.EnvSpecificInfo.GetDevfileObj().GetMetadataName()
|
||||||
|
|
||||||
|
o.clientset.KubernetesClient.SetNamespace(o.Context.GetProject())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *LogsOptions) Validate() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *LogsOptions) Run(ctx context.Context) error {
|
||||||
|
containersLogs, err := o.clientset.LogsClient.DevModeLogs(o.componentName, o.Context.GetProject())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
uniqueContainerNames := map[string]struct{}{}
|
||||||
|
for _, entry := range containersLogs {
|
||||||
|
for container, logs := range entry {
|
||||||
|
uniqueName := getUniqueContainerName(container, uniqueContainerNames)
|
||||||
|
uniqueContainerNames[uniqueName] = struct{}{}
|
||||||
|
err = printLogs(uniqueName, logs, o.out)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUniqueContainerName(name string, uniqueNames map[string]struct{}) string {
|
||||||
|
if _, ok := uniqueNames[name]; ok {
|
||||||
|
// name already present in uniqueNames; find another name
|
||||||
|
// first check if last character in name is a number; if so increment it, else append name with 1
|
||||||
|
last, err := strconv.Atoi(string(name[len(name)-1]))
|
||||||
|
if err == nil {
|
||||||
|
last++
|
||||||
|
name = fmt.Sprintf("%s[%d]", name[:len(name)-1], last)
|
||||||
|
} else {
|
||||||
|
last = 1
|
||||||
|
name = fmt.Sprintf("%s[%d]", name, last)
|
||||||
|
}
|
||||||
|
return getUniqueContainerName(name, uniqueNames)
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
// printLogs prints the logs of the containers with container name prefixed to the log message
|
||||||
|
func printLogs(containerName string, rd io.ReadCloser, out io.Writer) error {
|
||||||
|
color.Set(log.ColorPicker())
|
||||||
|
defer color.Unset()
|
||||||
|
scanner := bufio.NewScanner(rd)
|
||||||
|
scanner.Split(bufio.ScanLines)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
_, err := fmt.Fprintln(out, containerName+": "+line)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCmdLogs(name, fullname string) *cobra.Command {
|
||||||
|
o := NewLogsOptions()
|
||||||
|
logsCmd := &cobra.Command{
|
||||||
|
Use: name,
|
||||||
|
Short: "Show logs of all containers of the component",
|
||||||
|
Long: `odo logs shows logs of all containers of the component running in the Dev mode.
|
||||||
|
It prefixes each log message with the container name.`,
|
||||||
|
Example: fmt.Sprintf(logsExample, fullname),
|
||||||
|
Args: cobra.MaximumNArgs(0),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
genericclioptions.GenericRun(o, cmd, args)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
clientset.Add(logsCmd, clientset.LOGS, clientset.FILESYSTEM)
|
||||||
|
logsCmd.Annotations["command"] = "main"
|
||||||
|
logsCmd.SetUsageTemplate(odoutil.CmdUsageTemplate)
|
||||||
|
return logsCmd
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
package clientset
|
package clientset
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/redhat-developer/odo/pkg/logs"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/redhat-developer/odo/pkg/alizer"
|
"github.com/redhat-developer/odo/pkg/alizer"
|
||||||
@@ -33,6 +34,8 @@ import (
|
|||||||
const (
|
const (
|
||||||
// ALIZER instantiates client for pkg/alizer
|
// ALIZER instantiates client for pkg/alizer
|
||||||
ALIZER = "DEP_ALIZER"
|
ALIZER = "DEP_ALIZER"
|
||||||
|
// BINDING instantiates client for pkg/binding
|
||||||
|
BINDING = "DEP_BINDING"
|
||||||
// DELETE_COMPONENT instantiates client for pkg/component/delete
|
// DELETE_COMPONENT instantiates client for pkg/component/delete
|
||||||
DELETE_COMPONENT = "DEP_DELETE_COMPONENT"
|
DELETE_COMPONENT = "DEP_DELETE_COMPONENT"
|
||||||
// DEPLOY instantiates client for pkg/deploy
|
// DEPLOY instantiates client for pkg/deploy
|
||||||
@@ -47,6 +50,8 @@ const (
|
|||||||
KUBERNETES_NULLABLE = "DEP_KUBERNETES_NULLABLE"
|
KUBERNETES_NULLABLE = "DEP_KUBERNETES_NULLABLE"
|
||||||
// KUBERNETES instantiates client for pkg/kclient
|
// KUBERNETES instantiates client for pkg/kclient
|
||||||
KUBERNETES = "DEP_KUBERNETES"
|
KUBERNETES = "DEP_KUBERNETES"
|
||||||
|
// LOGS instantiates client for pkg/logs
|
||||||
|
LOGS = "DEP_LOGS"
|
||||||
// PREFERENCE instantiates client for pkg/preference
|
// PREFERENCE instantiates client for pkg/preference
|
||||||
PREFERENCE = "DEP_PREFERENCE"
|
PREFERENCE = "DEP_PREFERENCE"
|
||||||
// PROJECT instantiates client for pkg/project
|
// PROJECT instantiates client for pkg/project
|
||||||
@@ -57,8 +62,6 @@ const (
|
|||||||
STATE = "DEP_STATE"
|
STATE = "DEP_STATE"
|
||||||
// WATCH instantiates client for pkg/watch
|
// WATCH instantiates client for pkg/watch
|
||||||
WATCH = "DEP_WATCH"
|
WATCH = "DEP_WATCH"
|
||||||
// BINDING instantiates client for pkg/binding
|
|
||||||
BINDING = "DEP_BINDING"
|
|
||||||
/* Add key for new package here */
|
/* Add key for new package here */
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -70,6 +73,7 @@ var subdeps map[string][]string = map[string][]string{
|
|||||||
DEPLOY: {KUBERNETES},
|
DEPLOY: {KUBERNETES},
|
||||||
DEV: {WATCH},
|
DEV: {WATCH},
|
||||||
INIT: {ALIZER, FILESYSTEM, PREFERENCE, REGISTRY},
|
INIT: {ALIZER, FILESYSTEM, PREFERENCE, REGISTRY},
|
||||||
|
LOGS: {KUBERNETES},
|
||||||
PROJECT: {KUBERNETES_NULLABLE},
|
PROJECT: {KUBERNETES_NULLABLE},
|
||||||
REGISTRY: {FILESYSTEM, PREFERENCE},
|
REGISTRY: {FILESYSTEM, PREFERENCE},
|
||||||
STATE: {FILESYSTEM},
|
STATE: {FILESYSTEM},
|
||||||
@@ -86,6 +90,7 @@ type Clientset struct {
|
|||||||
FS filesystem.Filesystem
|
FS filesystem.Filesystem
|
||||||
InitClient _init.Client
|
InitClient _init.Client
|
||||||
KubernetesClient kclient.ClientInterface
|
KubernetesClient kclient.ClientInterface
|
||||||
|
LogsClient logs.Client
|
||||||
PreferenceClient preference.Client
|
PreferenceClient preference.Client
|
||||||
ProjectClient project.Client
|
ProjectClient project.Client
|
||||||
RegistryClient registry.Client
|
RegistryClient registry.Client
|
||||||
@@ -151,6 +156,9 @@ func Fetch(command *cobra.Command) (*Clientset, error) {
|
|||||||
if isDefined(command, INIT) {
|
if isDefined(command, INIT) {
|
||||||
dep.InitClient = _init.NewInitClient(dep.FS, dep.PreferenceClient, dep.RegistryClient, dep.AlizerClient)
|
dep.InitClient = _init.NewInitClient(dep.FS, dep.PreferenceClient, dep.RegistryClient, dep.AlizerClient)
|
||||||
}
|
}
|
||||||
|
if isDefined(command, LOGS) {
|
||||||
|
dep.LogsClient = logs.NewLogsClient(dep.KubernetesClient)
|
||||||
|
}
|
||||||
if isDefined(command, PROJECT) {
|
if isDefined(command, PROJECT) {
|
||||||
dep.ProjectClient = project.NewClient(dep.KubernetesClient)
|
dep.ProjectClient = project.NewClient(dep.KubernetesClient)
|
||||||
}
|
}
|
||||||
|
|||||||
53
tests/integration/devfile/cmd_logs_test.go
Normal file
53
tests/integration/devfile/cmd_logs_test.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package devfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
"github.com/onsi/gomega/gexec"
|
||||||
|
"github.com/redhat-developer/odo/tests/helper"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("odo logs command tests", func() {
|
||||||
|
var componentName string
|
||||||
|
var commonVar helper.CommonVar
|
||||||
|
|
||||||
|
var _ = BeforeEach(func() {
|
||||||
|
commonVar = helper.CommonBeforeEach()
|
||||||
|
componentName = helper.RandString(6)
|
||||||
|
helper.Chdir(commonVar.Context)
|
||||||
|
Expect(helper.VerifyFileExists(".odo/env/env.yaml")).To(BeFalse())
|
||||||
|
})
|
||||||
|
|
||||||
|
var _ = AfterEach(func() {
|
||||||
|
helper.CommonAfterEach(commonVar)
|
||||||
|
})
|
||||||
|
|
||||||
|
When("directory is empty", func() {
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
Expect(helper.ListFilesInDir(commonVar.Context)).To(HaveLen(0))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should error", func() {
|
||||||
|
output := helper.Cmd("odo", "logs").ShouldFail().Err()
|
||||||
|
Expect(output).To(ContainSubstring("this command cannot run in an empty directory"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("component is created and odo logs is executed", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context)
|
||||||
|
helper.Cmd("odo", "init", "--name", componentName, "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile.yaml")).ShouldPass()
|
||||||
|
Expect(helper.VerifyFileExists(".odo/env/env.yaml")).To(BeFalse())
|
||||||
|
})
|
||||||
|
It("should successfully show logs of the running component", func() {
|
||||||
|
err := helper.RunDevMode(func(session *gexec.Session, outContents []byte, errContents []byte, ports map[string]string) {
|
||||||
|
out := helper.Cmd("odo", "logs").ShouldPass().Out()
|
||||||
|
Expect(out).To(ContainSubstring("runtime: App started on PORT 3000"))
|
||||||
|
})
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user