diff --git a/.github/workflows/check_license.yml b/.github/workflows/check_license.yml index 831dc45..4c52427 100755 --- a/.github/workflows/check_license.yml +++ b/.github/workflows/check_license.yml @@ -18,6 +18,7 @@ jobs: with: # required to grab the history of the PR fetch-depth: 0 + submodules: 'true' - name: Get changed files run: | diff --git a/.github/workflows/kuksa-client.yml b/.github/workflows/kuksa-client.yml index 42579c7..f6fbcbb 100644 --- a/.github/workflows/kuksa-client.yml +++ b/.github/workflows/kuksa-client.yml @@ -15,14 +15,10 @@ name: kuksa_client on: push: - branches: [ master ] pull_request: - branches: [ master ] paths: - ".github/workflows/kuksa-client.yml" - "kuksa-client/**" - - "kuksa_certificates/**" - - "proto/**" workflow_dispatch: concurrency: @@ -35,7 +31,7 @@ jobs: secrets: inherit build-docker: - runs-on: self-hosted + runs-on: ubuntu-latest needs: check_ghcr_push steps: @@ -43,13 +39,14 @@ jobs: with: # Fetch everything to get tags working as expected fetch-depth: 0 + submodules: 'true' - name: Docker meta id: meta uses: docker/metadata-action@v4 with: # list of Docker images to use as base name for tags images: | - ghcr.io/eclipse/kuksa.val/kuksa-client + ghcr.io/eclipse-kuksa/kuksa-python-sdk/kuksa-client # generate Docker tags based on the following events/attributes tags: | type=ref,event=branch @@ -59,12 +56,12 @@ jobs: type=semver,pattern={{major}} # only needed for runners without buildx setup, will be slow - #- name: Set up QEMU - # uses: docker/setup-qemu-action@v2 + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 - #- name: Set up Docker Buildx - # id: buildx - # uses: docker/setup-buildx-action@v2 + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v2 - name: Log in to the Container registry if: needs.check_ghcr_push.outputs.push == 'true' @@ -90,7 +87,6 @@ jobs: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - # Push currently disabled as it results in 413 Request Entity Too Large - name: Build ephemeral KUKSA command line client docker and push to ttl.sh if: needs.check_ghcr_push.outputs.push == 'false' uses: docker/build-push-action@v4 @@ -101,20 +97,23 @@ jobs: context: . file: kuksa-client/Dockerfile push: true - tags: "ttl.sh/kuksa.val/kuksa-client-${{github.sha}}" + tags: "ttl.sh/kuksa-python-sdk/kuksa-client-${{github.sha}}" labels: ${{ steps.meta.outputs.labels }} + # Shall later be a kuksa-actions call - name: Posting temporary container location - uses: ./.github/actions/post-container-location + uses: eclipse-kuksa/kuksa-actions/post-container-location@2 with: - image: ttl.sh/kuksa.val/kuksa-client-${{github.sha}} + image: ttl.sh/kuksa-python-sdk/kuksa-client-${{github.sha}} kuksa-client-test: runs-on: ubuntu-latest steps: - - name: Checkout kuksa.val + - name: Checkout kuksa-python-sdk uses: actions/checkout@v3 + with: + submodules: 'true' - name: Install pip run: | python -m pip --quiet --no-input install --upgrade pip diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index fb1b4ed..028856d 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -15,6 +15,7 @@ jobs: with: # required to grab the history of the PR fetch-depth: 0 + submodules: 'true' - uses: actions/setup-python@v3 - uses: pre-commit/action@v3.0.0 with: diff --git a/kuksa-client/.gitignore b/.gitignore similarity index 100% rename from kuksa-client/.gitignore rename to .gitignore diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e6afa8a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "submodules/kuksa.val"] + path = submodules/kuksa.val + url = https://github.com/eclipse/kuksa.val +[submodule "submodules/kuksa-common"] + path = submodules/kuksa-common + url = https://github.com/eclipse-kuksa/kuksa-common diff --git a/LICENSE b/LICENSE index f49a4e1..261eeb9 100644 --- a/LICENSE +++ b/LICENSE @@ -198,4 +198,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. diff --git a/README.md b/README.md index 8c01195..85427eb 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,436 @@ -# KUKSA.VAL -![kuksa.val Logo](./doc/pictures/logo.png) +# KUKSA Python SDK +![kuksa.val Logo](./docs/pictures/logo.png) -This is KUKSA.val, the KUKSA **V**ehicle **A**bstraction **L**ayer. +**Note: The KUKSA project is currently in the process of moving the KUKSA Python SDK (kuksa-client)** +**from the [kuksa.val](https://github.com/eclipse/kuksa.val/tree/master/kuksa-client) repository** +**to this repository. This repository is still in *draft* status!** +**Use kuksa.val for development, pull requests and issues until kuksa-client has been removed** +**from that repository!** -KUKSA.val provides in-vehicle software components for working with in-vehicle signals modelled using the [COVESA VSS data model](https://github.com/COVESA/vehicle_signal_specification). +KUKSA Python Client and SDK is a part of the open source project [Eclipse KUKSA](https://www.eclipse.org/kuksa/). +More about Eclipse KUKSA can be found in the [repository](https://github.com/eclipse/kuksa.val). -If you are new here, try the [Quickstart](doc/quickstart.md), which should not take more than 10 min of your time. +## Introduction +KUKSA Python Client and SDK provides both a command-line interface (CLI) and a standalone library to interact with either +[KUKSA Server](https://github.com/eclipse/kuksa.val/tree/master/kuksa-val-server) or +[KUKSA Databroker](https://github.com/eclipse/kuksa.val/tree/master/kuksa_databroker). -[![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](https://opensource.org/licenses/Apache-2.0) -[![Gitter](https://img.shields.io/gitter/room/kuksa-val/community)](https://gitter.im/kuksa-val/community) -[![Build kuksa-val-server](https://github.com/eclipse/kuksa.val/actions/workflows/kuksa_val_docker.yml/badge.svg)](https://github.com/eclipse/kuksa.val/actions/workflows/kuksa_val_docker.yml?query=branch%3Amaster) -[![Build kuksa-databroker](https://github.com/eclipse/kuksa.val/actions/workflows/kuksa_databroker_build.yml/badge.svg)](https://github.com/eclipse/kuksa.val/actions/workflows/kuksa_databroker_build.yml?query=branch%3Amaster) -[![codecov](https://codecov.io/gh/eclipse/kuksa.val/branch/master/graph/badge.svg?token=M4FT175771)](https://codecov.io/gh/eclipse/kuksa.val) +## Installing the client and starting its CLI -KUKSA.val contains several components +The fastest way to start using KUKSA Python Client is to install a pre-built version from pypi.org: -| Component | Description | -| -------------- | ----------- | -| [KUKSA Databroker](./kuksa_databroker) | Efficient in-vehicle signal broker written in RUST providing authorized access to VSS data using gRPC -| [KUKSA Server](kuksa-val-server) | Feature rich in-vehicle data server written in C++ providing authorized access to VSS data using W3C VISS websocket protocol -| [KUKSA Python Client and SDK](./kuksa-client) | Command line tool to interactively explore and modify the VSS data points and data structure. Python library for easy interaction with KUKSA Databroker and Server -| [KUKSA GO Client](./kuksa_go_client) | Example client written in the [GO](https://go.dev/) programming language for easy interaction with KUKSA Databroker and Server -| [Example Applications](./kuksa_apps) | Some example apps for different programming languages and frameworks -| [Feeders and Providers](https://github.com/eclipse/kuksa.val.feeders/) | Multiple feeders and providers for exchanging vehicle data with KUKSA databroker and Server +```console +pip install kuksa-client +``` -## More information +If you want to install from sources instead see [Building and running a local version](#building-and-running-a-local-version). + +After you have installed the kuksa-client package via pip you can run the test client CLI directly by executing: + +```console +kuksa-client +``` + +With default CLI arguments, the client will try to connect to a local Databroker, e.g. a server supporting the `kuksa.val.v1` protocol without using TLS. This is equivalent to executing + +```console +kuksa-client grpc://127.0.0.1:55555 +``` + + +If everything works as expected and the server can be contacted you will get an output similar to below. + + +```console +Welcome to Kuksa Client version + + `-:+o/shhhs+:` + ./oo/+o/``.-:ohhs- + `/o+- /o/ `.. :yho` + +o/ /o/ oho ohy` + :o+ /o/`+hh. sh+ + +o: /oo+o+` /hy + +o: /o+/oo- +hs + .oo` oho `oo- .hh: + :oo. oho -+: -hh/ + .+o+-`oho `:shy- + ./o/ohy//+oyhho- + `-/+oo+/:. + +Default tokens directory: /some/path/kuksa_certificates/jwt + +Connecting to VSS server at 127.0.0.1 port 55555 using KUKSA GRPC protocol. +TLS will not be used. +INFO 2023-09-15 18:48:13,415 kuksa_client.grpc No Root CA present, it will not be posible to use a secure connection! +INFO 2023-09-15 18:48:13,415 kuksa_client.grpc.aio Establishing insecure channel +gRPC channel connected. +Test Client> +``` + +If you wish to connect to a VISS server e.g. `kuksa-val-server` (not using TLS), you should instead run: + +```console +kuksa-client ws://127.0.0.1:8090 +``` + +### TLS with databroker + +KUKSA Client uses TLS to connect to databroker when the schema part of the server URI is `grpcs`, i.e. a valid command to connect to a TLS enabled local databroker is + +``` +kuksa-client grpcs://localhost:55555 +``` + +By default the KUKSA example Root CA and Client keys are used, but client keys have no effect currently as mutual authentication is not supported by KUKSA Databroker or KUKSA Server. + + +This call with all parameters specified give same effect: + +``` +kuksa-client --certificate ../kuksa_certificates/Client.pem --keyfile ../kuksa_certificates/Client.key --cacertificate ./kuksa_certificates/CA.pem grpcs://localhost:55555 +``` + +There is actually no reason to specify client key and certificate, as mutual authentication is not supported in KUKSA Databroker, +so the command can be simplified like this: + +``` +kuksa-client --cacertificate ./kuksa_certificates/CA.pem grpcs://localhost:55555 +``` + +The example server protocol list 127.0.0.1 as an alternative name, but the TLS-client currently used does not accept it, +instead a valid server name must be given as argument. +Currently `Server` and `localhost` are valid names from the example certificates. + +``` +kuksa-client --cacertificate ../kuksa_certificates/CA.pem --tls-server-name Server grpcs://127.0.0.1:55555 +``` + +### TLS with val-server +Val-server also supports TLS. KUKSA Client uses TLS to connect to val-server when the schema part of the server URI is `wss`. A valid command to connect to a local TLS enabled val-server is + +``` +kuksa-client wss://localhost:8090 +``` + +This corresponds to this call: + +``` +kuksa-client --cacertificate ../kuksa_certificates/CA.pem wss://localhost:8090 +``` + +In some environments the `--tls-server-name` argument must be used to specify alternative server name +if connecting to the server by numerical IP address like `wss://127.0.0.1:8090`. + +### Authorizing against KUKSA Server +If the connected KUKSA Server or KUKSA Databroker require authorization the first step after a connection is made is to authorize. KUKSA Server and KUKSA Databroker use different token formats. + +The jwt tokens for testing can either be found under [../kuksa_certificates/jwt](../kuksa_certificates/jwt) +or you can also use following command inside `kuksa-client` to find the via `pip` installed certificate directory. + +```console +Test Client> printTokenDir +``` +Select one of the tokens and use the `authorize` command like below: + +```console +Test Client> authorize /some/path/kuksa_certificates/jwt/super-admin.json.token +``` + +### Authorizing against KUKSA Databroker + +If connecting to Databroker the command `printTokenDir` is not much help as it shows the default token directories +for KUKSA Server example tokens. If the KUKSA Databroker use default example tokens then one of the +tokens in [../jwt](../jwt) can be used, like in the example below: + +```console +Test Client> authorize /some/path/jwt/provide-all.token +``` + +## Usage Instructions + +Refer help for further information + +```console +Test Client> help -v + +Documented commands (use 'help -v' for verbose/'help ' for details): + +Communication Set-up Commands +================================================================================ +authorize Authorize the client to interact with the server +connect Connect to a VSS server +disconnect Disconnect from the VISS/gRPC Server +getServerAddress Gets the IP Address for the VISS/gRPC Server + +Info Commands +================================================================================ +info Show summary info of the client +printTokenDir Show default token directory +version Show version of the client + +Kuksa Interaction Commands +================================================================================ +getMetaData Get MetaData of the path +getTargetValue Get the value of a path +getTargetValues Get the value of given paths +getValue Get the value of a path +getValues Get the value of given paths +setTargetValue Set the target value of a path +setTargetValues Set the target value of given paths +setValue Set the value of a path +setValues Set the value of given paths +subscribe Subscribe the value of a path +subscribeMultiple Subscribe to updates of given paths +unsubscribe Unsubscribe an existing subscription +updateMetaData Update MetaData of a given path +updateVSSTree Update VSS Tree Entry + +``` + +This is an example showing how some of the commands can be used: + +![try kuksa-client out](https://raw.githubusercontent.com/eclipse/kuksa.val/master/doc/pictures/testclient_basic.gif "test client usage") + +### Syntax for specifying data in the command line interface + +Values used as argument to for example `setValue` shall match the type given. Quotes (single and double) are +generally not needed, except in a few special cases. A few valid examples on setting float is shown below: + +``` +setValue Vehicle.Speed 43 +setValue Vehicle.Speed "45" +setValue Vehicle.Speed '45.2' +``` + +For strings escaped quotes are needed if you want quotes to be sent to Server/Databroker, like if you want to store +`Almost "red"` as value. Alternatively you can use outer single quotes and inner double quotes. + +*NOTE: KUKSA Server and Databroker currently handle (escaped) quotes in strings differently!* +*The behavior described below is in general correct for KUKSA Databroker, but result may be different if interacting with KUKSA Server!* +*For consistent behavior it is recommended not to include (escaped) quotes in strings, except when needed to separate values* + +The two examples below are equal: + +``` +setValue Vehicle.Cabin.Light.InteractiveLightBar.Effect 'Almost \"red\"' +setValue Vehicle.Cabin.Light.InteractiveLightBar.Effect 'Almost "red"' +``` + +Alternatively you can use inner single quotes, but then the value will be represented by double quotes (`Almost "blue"`) +when stored anyhow. + +``` +setValue Vehicle.Cabin.Light.InteractiveLightBar.Effect "Almost 'blue'" +setValue Vehicle.Cabin.Light.InteractiveLightBar.Effect "Almost \'blue\'" +``` + +If not using outer quotes the inner quotes will be lost, the examples below are equal. +Leading/trailing spaces are ignored. + +``` +setValue Vehicle.Cabin.Light.InteractiveLightBar.Effect Almost 'green' +setValue Vehicle.Cabin.Light.InteractiveLightBar.Effect Almost green +setValue Vehicle.Cabin.Light.InteractiveLightBar.Effect 'Almost green' +setValue Vehicle.Cabin.Light.InteractiveLightBar.Effect "Almost green" +setValue Vehicle.Cabin.Light.InteractiveLightBar.Effect 'Almost green ' +``` + +It is possible to set array values. In general the value should be a valid JSON representation of the array. +For maximum compatibility for both KUKSA Server and KUKSA Databroker the following recommendations applies: + +* Always use single quotes around the array value. For some cases, like if there is no blanks or comma in the value, it is not needed, but it is good practice. +* Always use double quotes around string values. +* Never use single quotes inside string values +* Double quotes inside string values are allowed but must be escaped (`\"`) + +Some examples supported by both KUKSA databroker and KUKSA Server are shown below + +Setting a string array in KUKSA Databroker with simple identifiers is not a problem. +Also not if they contain blanks + +``` +// Array with two string elements +setValue Vehicle.OBD.DTCList '["abc","def"]' +// Array with two int elements (Note no quotes) +setValue Vehicle.SomeInt '[123,456]' +// Array with two elements, "hello there" and "def" +setValue Vehicle.OBD.DTCList '["hello there","def"]' +// Array with doubl quotes in string value; hello "there" +setValue Vehicle.OBD.DTCList '["hello, \"there\"","def"]' +``` + +### Updating VSS Structure + +Using the test client, it is also possible to update and extend the VSS data structure. +More details can be found [here](https://github.com/eclipse/kuksa.val/blob/master/doc/KUKSA.val_server/liveUpdateVSSTree.md). + +**Note**: You can also use `setValue` to change the value of an array, but the value should not contains any non-quoted spaces. Consider the following examples: + +```console +Test Client> setValue Vehicle.OBD.DTCList ["dtc1","dtc2"] +{ + "action": "set", + "requestId": "f7b199ce-4d86-4759-8d9a-d6f8f935722d", + "ts": "2022-03-22T17:19:34.1647965974Z" +} + +Test Client> setValue Vehicle.OBD.DTCList '["dtc1", "dtc2"]' +{ + "action": "set", + "requestId": "d4a19322-67d8-4fad-aa8a-2336404414be", + "ts": "2022-03-22T17:19:44.1647965984Z" +} + +Test Client> setValue Vehicle.OBD.DTCList ["dtc1", "dtc2"] +usage: setValue [-h] Path Value +setValue: error: unrecognized arguments: dtc2 ] +``` + +## Building and running a local version + +For development purposes it may be necessary to customize the code for the client and run a locally built version. To be able to build all submodules must be present, and you build from the `kuksa-client` folder. + +```console +git submodule update --init +cd kuksa-client +``` + +First we suggest you create a dedicated [python virtual environment](https://docs.python.org/3/library/venv.html) for kuksa-client: + +```console +mkdir --parents ~/.venv +python3 -m venv ~/.venv/kuksa-client +source ~/.venv/kuksa-client/bin/activate # Run this every time you want to activate kuksa-client's virtual environment +``` + +Your prompt should change to somehting indicating you are in the virutal environment now, e.g. + +```console +(kuksa-client) $ +``` +Inside the virtual environment install the dependencies +```console +pip install --upgrade pip +``` + +Now in order to ensure local `*.py` files will be used when running the client, we need to install kuksa-client in editable mode: + +```console +pip install -r requirements.txt -e . +``` + +If you wish to also install test dependencies, run instead: +```console +pip install -r test-requirements.txt -e ".[test]" +``` + +If you ever wish to upgrade provided requirements, see [Requirements](docs/requirements.md). + +Now you should be able to start using `kuksa-client`: +```console +kuksa-client --help +``` + +Whenever you want to exit kuksa-client's virtual environment, simply run: +```console +deactivate +``` + +## Using Docker +You can build a docker image of the testclient using the [`Dockerfile`](./Dockerfile). +Not the most effcient way to pack a small python script, but it is easy to get started. +The Dockerfile needs to be executed on the parent directory (so it include the needed certificates and `pip` package configuration). + + +```console +cd /some/dir/kuksa-python-sdk/ +docker build -f kuksa-client/Dockerfile -t kuksa-client:latest . +``` + +To run the newly built image: + +```console +docker run --rm -it --net=host kuksa-client:latest --help +``` + +Notes: +- `--rm` ensures we do not keep the docker container lying around after closing kuksa-client and `--net=host` makes sure you can reach locally running kuksa.val-server or kuksa-val docker with port forwarding on the host using the default `127.0.0.1` address. +- CLI arguments that follow image name (e.g. `kuksa-client:latest`) will be passed through to kuksa-client entry point (e.g. `--help`). + +## Running test suite & quality checks + +This project uses pytest as its test framework and pylint as its linter. +To run the test suite: + +```console +pytest +``` + +To run the linter: +```console +pylint kuksa_client +``` + +## Python library + +`kuksa-client` also provides a library to allow you to develop your own application that interacts with either +`kuksa-val-server` or `kuksa_databroker`. + + +### Usage + +Import library's main package. +```python +>>> import kuksa_client +>>> kuksa_client.__version__ +'' +``` + +This package holds different APIs depending on your application's requirements. +For more information, see ([Documentation](https://github.com/eclipse/kuksa.val/blob/master/kuksa-client/docs/main.md)). + + +### TLS configuration + +Clients like [KUKSA CAN Feeder](https://github.com/eclipse/kuksa.val.feeders/tree/main/dbc2val) +that use KUKSA Client library must typically set the path to the root CA certificate. +If the path is set the VSSClient will try to establish a secure connection. + +``` +# Shall TLS be used (default False for Databroker, True for KUKSA Server) +# tls = False +tls = True + +# TLS-related settings +# Path to root CA, needed if using TLS +root_ca_path=../../kuksa.val/kuksa_certificates/CA.pem +# Server name, typically only needed if accessing server by IP address like 127.0.0.1 +# and typically only if connection to KUKSA Databroker +# If using KUKSA example certificates the names "Server" or "localhost" can be used. +# tls_server_name=Server +``` + +## Troubleshooting + +1. The server/data broker is listening on its port but my client is unable to connect to it and returns an error: +```console +Error: Websocket could not be connected or the gRPC channel could not be created. +``` +If you're running both client and server on your local host, make sure that: +- `localhost` domain name resolution is configured properly on your host. +- You are not using any proxies for localhost e.g. setting the `no_proxy` environment variable to `localhost,127.0.0.1`. +- If you are using the `gRPC` protocol in secure mode, the server certificate should have `CN = localhost` in its subject. + +2. ``ImportError: cannot import name 'types_pb2' from 'kuksa.val.v1'``: +It sometimes happens that ``_pb2*.py`` files are not generated on editable installations of kuksa_client. +In order to manually generate those files and get more details if anything fails, run: +```console +python setup.py build_pb2 +``` -* [KUKSA.val TLS Concept](doc/tls.md) ## Pre-commit set up This repository is set up to use [pre-commit](https://pre-commit.com/) hooks. diff --git a/kuksa-client/docs/examples/async-grpc.md b/docs/examples/async-grpc.md similarity index 100% rename from kuksa-client/docs/examples/async-grpc.md rename to docs/examples/async-grpc.md diff --git a/kuksa-client/docs/examples/sync-grpc.md b/docs/examples/sync-grpc.md similarity index 100% rename from kuksa-client/docs/examples/sync-grpc.md rename to docs/examples/sync-grpc.md diff --git a/kuksa-client/docs/examples/threaded.md b/docs/examples/threaded.md similarity index 100% rename from kuksa-client/docs/examples/threaded.md rename to docs/examples/threaded.md diff --git a/kuksa-client/docs/main.md b/docs/main.md similarity index 100% rename from kuksa-client/docs/main.md rename to docs/main.md diff --git a/docs/pictures/logo.png b/docs/pictures/logo.png new file mode 100644 index 0000000..522d23e Binary files /dev/null and b/docs/pictures/logo.png differ diff --git a/kuksa-client/docs/requirements.md b/docs/requirements.md similarity index 100% rename from kuksa-client/docs/requirements.md rename to docs/requirements.md diff --git a/kuksa-client/README.md b/kuksa-client/README.md deleted file mode 100644 index 9d882b5..0000000 --- a/kuksa-client/README.md +++ /dev/null @@ -1,419 +0,0 @@ -# KUKSA Python Client and SDK -![kuksa.val Logo](https://raw.githubusercontent.com/eclipse/kuksa.val/0.2.5/doc/pictures/logo.png) - -KUKSA Python Client and SDK is a part of the open source project [Eclipse KUKSA](https://www.eclipse.org/kuksa/). -More about Eclipse KUKSA can be found in the [repository](https://github.com/eclipse/kuksa.val). - -## Introduction - -KUKSA Python Client and SDK provides both a command-line interface (CLI) and a standalone library to interact with either -[KUKSA Server](https://github.com/eclipse/kuksa.val/tree/master/kuksa-val-server) or -[KUKSA Databroker](https://github.com/eclipse/kuksa.val/tree/master/kuksa_databroker). - - -## Installing the client and starting its CLI - -The fastest way to start using KUKSA Python Client is to install a pre-built version from pypi.org: - -```console -pip install kuksa-client -``` - -If you want to install from sources instead see [Building and running a local version](#building-and-running-a-local-version). - -After you have installed the kuksa-client package via pip you can run the test client CLI directly by executing: - -```console -kuksa-client -``` - -With default CLI arguments, the client will try to connect to a local Databroker, e.g. a server supporting the `kuksa.val.v1` protocol without using TLS. This is equivalent to executing - -```console -kuksa-client grpc://127.0.0.1:55555 -``` - - -If everything works as expected and the server can be contacted you will get an output similar to below. - - -```console -Welcome to Kuksa Client version - - `-:+o/shhhs+:` - ./oo/+o/``.-:ohhs- - `/o+- /o/ `.. :yho` - +o/ /o/ oho ohy` - :o+ /o/`+hh. sh+ - +o: /oo+o+` /hy - +o: /o+/oo- +hs - .oo` oho `oo- .hh: - :oo. oho -+: -hh/ - .+o+-`oho `:shy- - ./o/ohy//+oyhho- - `-/+oo+/:. - -Default tokens directory: /some/path/kuksa_certificates/jwt - -Connecting to VSS server at 127.0.0.1 port 55555 using KUKSA GRPC protocol. -TLS will not be used. -INFO 2023-09-15 18:48:13,415 kuksa_client.grpc No Root CA present, it will not be posible to use a secure connection! -INFO 2023-09-15 18:48:13,415 kuksa_client.grpc.aio Establishing insecure channel -gRPC channel connected. -Test Client> -``` - -If you wish to connect to a VISS server e.g. `kuksa-val-server` (not using TLS), you should instead run: - -```console -kuksa-client ws://127.0.0.1:8090 -``` - -### TLS with databroker - -KUKSA Client uses TLS to connect to databroker when the schema part of the server URI is `grpcs`, i.e. a valid command to connect to a TLS enabled local databroker is - -``` -kuksa-client grpcs://localhost:55555 -``` - -By default the KUKSA example Root CA and Client keys are used, but client keys have no effect currently as mutual authentication is not supported by KUKSA Databroker or KUKSA Server. - - -This call with all parameters specified give same effect: - -``` -kuksa-client --certificate ../kuksa_certificates/Client.pem --keyfile ../kuksa_certificates/Client.key --cacertificate ./kuksa_certificates/CA.pem grpcs://localhost:55555 -``` - -There is actually no reason to specify client key and certificate, as mutual authentication is not supported in KUKSA Databroker, -so the command can be simplified like this: - -``` -kuksa-client --cacertificate ./kuksa_certificates/CA.pem grpcs://localhost:55555 -``` - -The example server protocol list 127.0.0.1 as an alternative name, but the TLS-client currently used does not accept it, -instead a valid server name must be given as argument. -Currently `Server` and `localhost` are valid names from the example certificates. - -``` -kuksa-client --cacertificate ../kuksa_certificates/CA.pem --tls-server-name Server grpcs://127.0.0.1:55555 -``` - -### TLS with val-server -Val-server also supports TLS. KUKSA Client uses TLS to connect to val-server when the schema part of the server URI is `wss`. A valid command to connect to a local TLS enabled val-server is - -``` -kuksa-client wss://localhost:8090 -``` - -This corresponds to this call: - -``` -kuksa-client --cacertificate ../kuksa_certificates/CA.pem wss://localhost:8090 -``` - -In some environments the `--tls-server-name` argument must be used to specify alternative server name -if connecting to the server by numerical IP address like `wss://127.0.0.1:8090`. - -### Authorizing against KUKSA Server -If the connected KUKSA Server or KUKSA Databroker require authorization the first step after a connection is made is to authorize. KUKSA Server and KUKSA Databroker use different token formats. - -The jwt tokens for testing can either be found under [../kuksa_certificates/jwt](../kuksa_certificates/jwt) -or you can also use following command inside `kuksa-client` to find the via `pip` installed certificate directory. - -```console -Test Client> printTokenDir -``` -Select one of the tokens and use the `authorize` command like below: - -```console -Test Client> authorize /some/path/kuksa_certificates/jwt/super-admin.json.token -``` - -### Authorizing against KUKSA Databroker - -If connecting to Databroker the command `printTokenDir` is not much help as it shows the default token directories -for KUKSA Server example tokens. If the KUKSA Databroker use default example tokens then one of the -tokens in [../jwt](../jwt) can be used, like in the example below: - -```console -Test Client> authorize /some/path/jwt/provide-all.token -``` - -## Usage Instructions - -Refer help for further information - -```console -Test Client> help -v - -Documented commands (use 'help -v' for verbose/'help ' for details): - -Communication Set-up Commands -================================================================================ -authorize Authorize the client to interact with the server -connect Connect to a VSS server -disconnect Disconnect from the VISS/gRPC Server -getServerAddress Gets the IP Address for the VISS/gRPC Server - -Info Commands -================================================================================ -info Show summary info of the client -printTokenDir Show default token directory -version Show version of the client - -Kuksa Interaction Commands -================================================================================ -getMetaData Get MetaData of the path -getTargetValue Get the value of a path -getTargetValues Get the value of given paths -getValue Get the value of a path -getValues Get the value of given paths -setTargetValue Set the target value of a path -setTargetValues Set the target value of given paths -setValue Set the value of a path -setValues Set the value of given paths -subscribe Subscribe the value of a path -subscribeMultiple Subscribe to updates of given paths -unsubscribe Unsubscribe an existing subscription -updateMetaData Update MetaData of a given path -updateVSSTree Update VSS Tree Entry - -``` - -This is an example showing how some of the commands can be used: - -![try kuksa-client out](https://raw.githubusercontent.com/eclipse/kuksa.val/master/doc/pictures/testclient_basic.gif "test client usage") - -### Syntax for specifying data in the command line interface - -Values used as argument to for example `setValue` shall match the type given. Quotes (single and double) are -generally not needed, except in a few special cases. A few valid examples on setting float is shown below: - -``` -setValue Vehicle.Speed 43 -setValue Vehicle.Speed "45" -setValue Vehicle.Speed '45.2' -``` - -For strings escaped quotes are needed if you want quotes to be sent to Server/Databroker, like if you want to store -`Almost "red"` as value. Alternatively you can use outer single quotes and inner double quotes. - -*NOTE: KUKSA Server and Databroker currently handle (escaped) quotes in strings differently!* -*The behavior described below is in general correct for KUKSA Databroker, but result may be different if interacting with KUKSA Server!* -*For consistent behavior it is recommended not to include (escaped) quotes in strings, except when needed to separate values* - -The two examples below are equal: - -``` -setValue Vehicle.Cabin.Light.InteractiveLightBar.Effect 'Almost \"red\"' -setValue Vehicle.Cabin.Light.InteractiveLightBar.Effect 'Almost "red"' -``` - -Alternatively you can use inner single quotes, but then the value will be represented by double quotes (`Almost "blue"`) -when stored anyhow. - -``` -setValue Vehicle.Cabin.Light.InteractiveLightBar.Effect "Almost 'blue'" -setValue Vehicle.Cabin.Light.InteractiveLightBar.Effect "Almost \'blue\'" -``` - -If not using outer quotes the inner quotes will be lost, the examples below are equal. -Leading/trailing spaces are ignored. - -``` -setValue Vehicle.Cabin.Light.InteractiveLightBar.Effect Almost 'green' -setValue Vehicle.Cabin.Light.InteractiveLightBar.Effect Almost green -setValue Vehicle.Cabin.Light.InteractiveLightBar.Effect 'Almost green' -setValue Vehicle.Cabin.Light.InteractiveLightBar.Effect "Almost green" -setValue Vehicle.Cabin.Light.InteractiveLightBar.Effect 'Almost green ' -``` - -It is possible to set array values. In general the value should be a valid JSON representation of the array. -For maximum compatibility for both KUKSA Server and KUKSA Databroker the following recommendations applies: - -* Always use single quotes around the array value. For some cases, like if there is no blanks or comma in the value, it is not needed, but it is good practice. -* Always use double quotes around string values. -* Never use single quotes inside string values -* Double quotes inside string values are allowed but must be escaped (`\"`) - -Some examples supported by both KUKSA databroker and KUKSA Server are shown below - -Setting a string array in KUKSA Databroker with simple identifiers is not a problem. -Also not if they contain blanks - -``` -// Array with two string elements -setValue Vehicle.OBD.DTCList '["abc","def"]' -// Array with two int elements (Note no quotes) -setValue Vehicle.SomeInt '[123,456]' -// Array with two elements, "hello there" and "def" -setValue Vehicle.OBD.DTCList '["hello there","def"]' -// Array with doubl quotes in string value; hello "there" -setValue Vehicle.OBD.DTCList '["hello, \"there\"","def"]' -``` - -### Updating VSS Structure - -Using the test client, it is also possible to update and extend the VSS data structure. -More details can be found [here](https://github.com/eclipse/kuksa.val/blob/master/doc/KUKSA.val_server/liveUpdateVSSTree.md). - -**Note**: You can also use `setValue` to change the value of an array, but the value should not contains any non-quoted spaces. Consider the following examples: - -```console -Test Client> setValue Vehicle.OBD.DTCList ["dtc1","dtc2"] -{ - "action": "set", - "requestId": "f7b199ce-4d86-4759-8d9a-d6f8f935722d", - "ts": "2022-03-22T17:19:34.1647965974Z" -} - -Test Client> setValue Vehicle.OBD.DTCList '["dtc1", "dtc2"]' -{ - "action": "set", - "requestId": "d4a19322-67d8-4fad-aa8a-2336404414be", - "ts": "2022-03-22T17:19:44.1647965984Z" -} - -Test Client> setValue Vehicle.OBD.DTCList ["dtc1", "dtc2"] -usage: setValue [-h] Path Value -setValue: error: unrecognized arguments: dtc2 ] -``` - -## Building and running a local version - -For development purposes it may be necessary to customize the code for the client and run a locally built version. -First we suggest you create a dedicated [python virtual environment](https://docs.python.org/3/library/venv.html) for kuksa-client: - -```console -mkdir --parents ~/.venv -python3 -m venv ~/.venv/kuksa-client -source ~/.venv/kuksa-client/bin/activate # Run this every time you want to activate kuksa-client's virtual environment -``` - -Your prompt should change to somehting indicating you are in the virutal environment now, e.g. - -```console -(kuksa-client) $ -``` -Inside the virtual environment install the dependencies -```console -pip install --upgrade pip -``` - -Now in order to ensure local `*.py` files will be used when running the client, we need to install kuksa-client in editable mode: - -```console -pip install -r requirements.txt -e . -``` - -If you wish to also install test dependencies, run instead: -```console -pip install -r test-requirements.txt -e ".[test]" -``` - -If you ever wish to upgrade provided requirements, see [Requirements](docs/requirements.md). - -Now you should be able to start using `kuksa-client`: -```console -kuksa-client --help -``` - -Whenever you want to exit kuksa-client's virtual environment, simply run: -```console -deactivate -``` - -## Using Docker -You can build a docker image of the testclient using the [`Dockerfile`](./Dockerfile). -Not the most effcient way to pack a small python script, but it is easy to get started. -The Dockerfile needs to be executed on the parent directory (so it include the needed certificates and `pip` package configuration). - - -```console -cd /some/dir/kuksa.val -docker build -f kuksa-client/Dockerfile -t kuksa-client:latest . -``` - -To run the newly built image: - -```console -docker run --rm -it --net=host kuksa-client:latest --help -``` - -Notes: -- `--rm` ensures we do not keep the docker container lying around after closing kuksa-client and `--net=host` makes sure you can reach locally running kuksa.val-server or kuksa-val docker with port forwarding on the host using the default `127.0.0.1` address. -- CLI arguments that follow image name (e.g. `kuksa-client:latest`) will be passed through to kuksa-client entry point (e.g. `--help`). - -## Running test suite & quality checks - -This project uses pytest as its test framework and pylint as its linter. -To run the test suite: - -```console -pytest -``` - -To run the linter: -```console -pylint kuksa_client -``` - -## Python library - -`kuksa-client` also provides a library to allow you to develop your own application that interacts with either -`kuksa-val-server` or `kuksa_databroker`. - - -### Usage - -Import library's main package. -```python ->>> import kuksa_client ->>> kuksa_client.__version__ -'' -``` - -This package holds different APIs depending on your application's requirements. -For more information, see ([Documentation](https://github.com/eclipse/kuksa.val/blob/master/kuksa-client/docs/main.md)). - - -### TLS configuration - -Clients like [KUKSA CAN Feeder](https://github.com/eclipse/kuksa.val.feeders/tree/main/dbc2val) -that use KUKSA Client library must typically set the path to the root CA certificate. -If the path is set the VSSClient will try to establish a secure connection. - -``` -# Shall TLS be used (default False for Databroker, True for KUKSA Server) -# tls = False -tls = True - -# TLS-related settings -# Path to root CA, needed if using TLS -root_ca_path=../../kuksa.val/kuksa_certificates/CA.pem -# Server name, typically only needed if accessing server by IP address like 127.0.0.1 -# and typically only if connection to KUKSA Databroker -# If using KUKSA example certificates the names "Server" or "localhost" can be used. -# tls_server_name=Server -``` - -## Troubleshooting - -1. The server/data broker is listening on its port but my client is unable to connect to it and returns an error: -```console -Error: Websocket could not be connected or the gRPC channel could not be created. -``` -If you're running both client and server on your local host, make sure that: -- `localhost` domain name resolution is configured properly on your host. -- You are not using any proxies for localhost e.g. setting the `no_proxy` environment variable to `localhost,127.0.0.1`. -- If you are using the `gRPC` protocol in secure mode, the server certificate should have `CN = localhost` in its subject. - -2. ``ImportError: cannot import name 'types_pb2' from 'kuksa.val.v1'``: -It sometimes happens that ``_pb2*.py`` files are not generated on editable installations of kuksa_client. -In order to manually generate those files and get more details if anything fails, run: -```console -python setup.py build_pb2 -``` diff --git a/kuksa-client/README.md b/kuksa-client/README.md new file mode 120000 index 0000000..32d46ee --- /dev/null +++ b/kuksa-client/README.md @@ -0,0 +1 @@ +../README.md \ No newline at end of file diff --git a/kuksa-client/kuksa/__init__.py b/kuksa-client/kuksa/__init__.py index e69de29..720b14f 100644 --- a/kuksa-client/kuksa/__init__.py +++ b/kuksa-client/kuksa/__init__.py @@ -0,0 +1,12 @@ +# /******************************************************************************** +# * Copyright (c) 2023 Contributors to the Eclipse Foundation +# * +# * See the NOTICE file(s) distributed with this work for additional +# * information regarding copyright ownership. +# * +# * This program and the accompanying materials are made available under the +# * terms of the Apache License 2.0 which is available at +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * SPDX-License-Identifier: Apache-2.0 +# ********************************************************************************/ diff --git a/kuksa-client/kuksa/val/__init__.py b/kuksa-client/kuksa/val/__init__.py index e69de29..720b14f 100644 --- a/kuksa-client/kuksa/val/__init__.py +++ b/kuksa-client/kuksa/val/__init__.py @@ -0,0 +1,12 @@ +# /******************************************************************************** +# * Copyright (c) 2023 Contributors to the Eclipse Foundation +# * +# * See the NOTICE file(s) distributed with this work for additional +# * information regarding copyright ownership. +# * +# * This program and the accompanying materials are made available under the +# * terms of the Apache License 2.0 which is available at +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * SPDX-License-Identifier: Apache-2.0 +# ********************************************************************************/ diff --git a/kuksa-client/kuksa/val/v1/README.md b/kuksa-client/kuksa/val/v1/README.md index e79237a..e3c7451 120000 --- a/kuksa-client/kuksa/val/v1/README.md +++ b/kuksa-client/kuksa/val/v1/README.md @@ -1 +1 @@ -../../../../proto/kuksa/val/v1/README.md \ No newline at end of file +../../../../submodules/kuksa.val/proto/kuksa/val/v1/README.md \ No newline at end of file diff --git a/kuksa-client/kuksa/val/v1/__init__.py b/kuksa-client/kuksa/val/v1/__init__.py index e69de29..720b14f 100644 --- a/kuksa-client/kuksa/val/v1/__init__.py +++ b/kuksa-client/kuksa/val/v1/__init__.py @@ -0,0 +1,12 @@ +# /******************************************************************************** +# * Copyright (c) 2023 Contributors to the Eclipse Foundation +# * +# * See the NOTICE file(s) distributed with this work for additional +# * information regarding copyright ownership. +# * +# * This program and the accompanying materials are made available under the +# * terms of the Apache License 2.0 which is available at +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * SPDX-License-Identifier: Apache-2.0 +# ********************************************************************************/ diff --git a/kuksa-client/kuksa/val/v1/types.proto b/kuksa-client/kuksa/val/v1/types.proto index d063a79..c006b2d 120000 --- a/kuksa-client/kuksa/val/v1/types.proto +++ b/kuksa-client/kuksa/val/v1/types.proto @@ -1 +1 @@ -../../../../proto/kuksa/val/v1/types.proto \ No newline at end of file +../../../../submodules/kuksa.val/proto/kuksa/val/v1/types.proto \ No newline at end of file diff --git a/kuksa-client/kuksa/val/v1/val.proto b/kuksa-client/kuksa/val/v1/val.proto index 04d53e8..a5c8ed0 120000 --- a/kuksa-client/kuksa/val/v1/val.proto +++ b/kuksa-client/kuksa/val/v1/val.proto @@ -1 +1 @@ -../../../../proto/kuksa/val/v1/val.proto \ No newline at end of file +../../../../submodules/kuksa.val/proto/kuksa/val/v1/val.proto \ No newline at end of file diff --git a/kuksa-client/kuksa_certificates b/kuksa-client/kuksa_certificates deleted file mode 120000 index 6e41da5..0000000 --- a/kuksa-client/kuksa_certificates +++ /dev/null @@ -1 +0,0 @@ -../kuksa_certificates \ No newline at end of file diff --git a/kuksa-client/kuksa_client/__init__.py b/kuksa-client/kuksa_client/__init__.py index 4a3064a..5a5ee90 100644 --- a/kuksa-client/kuksa_client/__init__.py +++ b/kuksa-client/kuksa_client/__init__.py @@ -44,7 +44,7 @@ def stop(self): self.backend.stop() # Do authorization by passing a jwt token or a token file - def authorize(self, token_or_tokenfile: Optional[str]=None, timeout=5): + def authorize(self, token_or_tokenfile: Optional[str] = None, timeout=5): return self.backend.authorize(token_or_tokenfile, timeout) # Update VSS Tree Entry diff --git a/kuksa-client/kuksa_client/__main__.py b/kuksa-client/kuksa_client/__main__.py index 3fd2f7b..032410b 100755 --- a/kuksa-client/kuksa_client/__main__.py +++ b/kuksa-client/kuksa_client/__main__.py @@ -39,7 +39,7 @@ from cmd2.utils import basic_complete from urllib.parse import urlparse -import kuksa_certificates +from kuksa_client import kuksa_server_certificates from kuksa_client import KuksaClientThread from kuksa_client import _metadata @@ -598,7 +598,7 @@ def connect(self): # Explain were we are connecting to: print( - f"Connecting to VSS server at {config['ip']} port {config['port']} \ + f"Connecting to VSS server at {config['ip'] } port {config['port'] } \ using {'KUKSA GRPC' if config['protocol'] == 'grpc' else 'VISS' } protocol." ) print(f"TLS will {'not be' if config['insecure'] else 'be'} used.") @@ -641,9 +641,9 @@ def do_connect(self, args): def getDefaultTokenDir(self): try: - return os.path.join(kuksa_certificates.__certificate_dir__, "jwt") + return os.path.join(kuksa_server_certificates.__certificate_dir__, "jwt") except AttributeError: - guessTokenDir = os.path.join(scriptDir, "../kuksa_certificates/jwt") + guessTokenDir = os.path.join(scriptDir, "kuksa_server_certificates/jwt") if os.path.isdir(guessTokenDir): return guessTokenDir return "Unknown" diff --git a/kuksa-client/kuksa_client/_metadata.py b/kuksa-client/kuksa_client/_metadata.py index 17c044c..3560041 100644 --- a/kuksa-client/kuksa_client/_metadata.py +++ b/kuksa-client/kuksa_client/_metadata.py @@ -47,7 +47,6 @@ metadata = importlib_metadata.metadata("kuksa_client") - __title__ = metadata["name"] __summary__ = metadata["summary"] __uri__ = metadata["home-page"] diff --git a/kuksa-client/kuksa_client/cli_backend/__init__.py b/kuksa-client/kuksa_client/cli_backend/__init__.py index f757cd6..2cfe51e 100644 --- a/kuksa-client/kuksa_client/cli_backend/__init__.py +++ b/kuksa-client/kuksa_client/cli_backend/__init__.py @@ -17,9 +17,7 @@ ######################################################################## import pathlib - -import kuksa_certificates - +from kuksa_client import kuksa_server_certificates class Backend: @@ -30,7 +28,7 @@ def __init__(self, config): self.insecure = config.getboolean('insecure', False) except AttributeError: self.insecure = config.get('insecure', False) - self.default_cert_path = pathlib.Path(kuksa_certificates.__path__[0]) + self.default_cert_path = pathlib.Path(kuksa_server_certificates.__path__[0]) self.cacertificate = config.get( 'cacertificate', str(self.default_cert_path / 'CA.pem')) self.certificate = config.get('certificate', str( diff --git a/kuksa-client/kuksa_client/kuksa_server_certificates b/kuksa-client/kuksa_client/kuksa_server_certificates new file mode 120000 index 0000000..a02c477 --- /dev/null +++ b/kuksa-client/kuksa_client/kuksa_server_certificates @@ -0,0 +1 @@ +../../submodules/kuksa.val/kuksa_certificates \ No newline at end of file diff --git a/kuksa-client/kuksa_client/ws/__init__.py b/kuksa-client/kuksa_client/ws/__init__.py index e69de29..720b14f 100644 --- a/kuksa-client/kuksa_client/ws/__init__.py +++ b/kuksa-client/kuksa_client/ws/__init__.py @@ -0,0 +1,12 @@ +# /******************************************************************************** +# * Copyright (c) 2023 Contributors to the Eclipse Foundation +# * +# * See the NOTICE file(s) distributed with this work for additional +# * information regarding copyright ownership. +# * +# * This program and the accompanying materials are made available under the +# * terms of the Apache License 2.0 which is available at +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * SPDX-License-Identifier: Apache-2.0 +# ********************************************************************************/ diff --git a/kuksa-client/setup.cfg b/kuksa-client/setup.cfg index c1103ae..d0d18e2 100644 --- a/kuksa-client/setup.cfg +++ b/kuksa-client/setup.cfg @@ -3,12 +3,12 @@ name = kuksa_client author = Eclipse KUKSA Project author_email = kuksa-dev@eclipse.org description = KUKSA Python Client and SDK -long_description = file:./README.md +long_description = file:README.md long_description_content_type = text/markdown -url=https://github.com/eclipse/kuksa.val +url=https://github.com/eclipse-kuksa/kuksa-python-sdk project_urls= - Source=https://github.com/eclipse/kuksa.val/tree/master/kuksa-client - Bug Tracker=https://github.com/eclipse/kuksa.val/issues + Source=https://github.com/eclipse-kuksa/kuksa-python-sdk + Bug Tracker=https://github.com/eclipse-kuksa/kuksa-python-sdk/issues classifiers = Intended Audience :: Developers Development Status :: 5 - Production/Stable @@ -44,9 +44,8 @@ test = kuksa_client = logging.ini logo -kuksa_certificates = - * - jwt/* + kuksa_server_certificates/* + kuksa_server_certificates/jwt/* [options.packages.find] where = . diff --git a/kuksa-client/tests/__init__.py b/kuksa-client/tests/__init__.py index e69de29..720b14f 100644 --- a/kuksa-client/tests/__init__.py +++ b/kuksa-client/tests/__init__.py @@ -0,0 +1,12 @@ +# /******************************************************************************** +# * Copyright (c) 2023 Contributors to the Eclipse Foundation +# * +# * See the NOTICE file(s) distributed with this work for additional +# * information regarding copyright ownership. +# * +# * This program and the accompanying materials are made available under the +# * terms of the Apache License 2.0 which is available at +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * SPDX-License-Identifier: Apache-2.0 +# ********************************************************************************/ diff --git a/submodules/kuksa-common b/submodules/kuksa-common new file mode 160000 index 0000000..495d627 --- /dev/null +++ b/submodules/kuksa-common @@ -0,0 +1 @@ +Subproject commit 495d627225dc09d8171afd13970da152ce8a2cf8 diff --git a/submodules/kuksa.val b/submodules/kuksa.val new file mode 160000 index 0000000..6690ca9 --- /dev/null +++ b/submodules/kuksa.val @@ -0,0 +1 @@ +Subproject commit 6690ca970a2f7dd8245bd00f6b10788eaad755b5 diff --git a/kuksa-client/test-roadster-elon.json b/test-roadster-elon.json similarity index 100% rename from kuksa-client/test-roadster-elon.json rename to test-roadster-elon.json