Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scala based admin tooling #3722

Merged
merged 39 commits into from
Jun 26, 2018
Merged

Scala based admin tooling #3722

merged 39 commits into from
Jun 26, 2018

Conversation

chetanmeh
Copy link
Member

@chetanmeh chetanmeh commented Jun 4, 2018

This PR implements some part of wskadmin in scala. See this thread for related discussion.

Description

Currently some of the admin toolings like wskadmin connect directly to CouchDB and hence would not work for setups where some other ArtifactStore is configured (like CosmosDBArtifactStore). To support duch deployments this PR implement some of the wskadmin features in a Scala based tooling which makes use of the ArtifactStore abstraction.

Usage

The tooling is packaged as an executable fat jar which packages all the requiored dependencies in a single jar and can be executed like

$./wskadmin-cli -c application-cli.conf user get guest

Here

  • wskadmin-cli - Executable fat jar. Gradle build would copy this to ${OPENWHISK_HOME}/bin folder
  • -c application-cli.conf - Configuration details. For now the db related configuration is specified here
include classpath("application.conf")

whisk {
  couchdb {
    protocol = "http"
    host     = "172.17.0.1"
    port     = "5984"
    username = "whisk_admin"
    password = "some_passw0rd"
    provider = "CouchDB"
    databases {
      WhiskAuth       = "whisk_local_subjects"
      WhiskEntity     = "whisk_local_whisks"
      WhiskActivation = "whisk_local_activations"
    }
  }
}

See readme for details

Commands Supported

User

manage users commands

  • create - create a user and show authorization key
  • delete - delete a user
  • get - get authorization key for user
  • whois - identify user from an authorization key
  • block - block one or more users
  • unblock - unblock one or more users
  • list - list authorization keys associated with a namespace

limits

manage namespace-specific limits

  • set - set limits for a given namespace
  • get - get limits for a given namespace (if none exist, system defaults apply)
  • delete - delete limits for a given namespace (system defaults apply)

db

work with dbs

  • - get - get contents of database

Key aspects

  • Requires the configuration provided in form of application.conf
  • Supports direct execution
  • Requires Java JDK to be present on the host

Implementation Details

Packaging

The tooling is packaged as an executable jar via Spring Boot Gradle Plugin. The plugin is used just for packaging and there is no dependency on Spring framework. Spring boot plugin creates a jar with following structure

|-- BOOT-INF
|   |-- classes - CLI Imp
|   `-- lib - Dependencies
|-- META-INF
`-- org    #Spring Boot classes

This allows having multiple files with same name like reference.conf by ensuring dependency jars remain separate.

Further it alse allows packaging the jar as an executable script. This enable launching jar like ./wskadmin-cli instead of using java -jar wskadmin-cli.jar approach for Linux and Mac setups. For windows users can still use the java -jar approach to launch the command.

CLI Option Parsing

It uses scallop library to parse the command line options. It also supports sub commands similar to Python argparse library

Open Points

  1. What should be the name of cli. Currently its named as wskadmin-cli
  2. Where should the code live. Currently its in core/admin

Related issue and scope

  • [] I opened an issue to propose and discuss this change (#????)

My changes affect the following components

  • API
  • Controller
  • Message Bus (e.g., Kafka)
  • Loadbalancer
  • Invoker
  • Intrinsic actions (e.g., sequences, conductors)
  • Data stores (e.g., CouchDB)
  • Tests
  • Deployment
  • CLI
  • General tooling
  • Documentation

Types of changes

  • Bug fix (generally a non-breaking change which closes an issue).
  • Enhancement or new feature (adds new functionality).
  • Breaking change (a bug fix or enhancement which changes existing behavior).

Checklist:

  • I signed an Apache CLA.
  • I reviewed the style guides and followed the recommendations (Travis CI will check :).
  • I added tests to cover my changes.
  • My changes require further changes to the documentation.
  • I updated the documentation where necessary.

@codecov-io
Copy link

codecov-io commented Jun 4, 2018

Codecov Report

Merging #3722 into master will increase coverage by 0.03%.
The diff coverage is 76.05%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3722      +/-   ##
==========================================
+ Coverage   75.13%   75.16%   +0.03%     
==========================================
  Files         132      136       +4     
  Lines        6140     6378     +238     
  Branches      376      392      +16     
==========================================
+ Hits         4613     4794     +181     
- Misses       1527     1584      +57
Impacted Files Coverage Δ
...ols/admin/src/main/scala/whisk/core/cli/Main.scala 26.38% <26.38%> (ø)
...rc/main/scala/whisk/core/cli/CommandMessages.scala 94.44% <94.44%> (ø)
...c/main/scala/whisk/core/database/UserCommand.scala 97.77% <97.77%> (ø)
...main/scala/whisk/core/database/LimitsCommand.scala 98.27% <98.27%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 76dadd2...6a3fc82. Read the comment docs.

banner("OpenWhisk admin command line tool")

val verbose = tally()
val configFile = opt[File](descr = "application.conf path")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @chetanmeh,

I'm wondering if there can be a default path of application.conf that we can automatically generate this during ansible deployment? i.e. step on setup couchdb.

Copy link
Member Author

@chetanmeh chetanmeh Jun 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That can be done. Another way I was thinking to have better support for Couch by reading whisk.properties as being done by current wskadmin. So scala tooling would also read that and seed in required typesafe properties such that for couch you need not pass in application.conf.

For others like Cosmos or later Mongo user can pass application.conf

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading whisk.properties also works fine, but is there any specific reason that we can't provide a consistent way? (not only supporting couch)
i.e. once user deploy/re-deploy/change the database, update the config for them.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No specific reason. So. far I was thinking that we "publish" the tool jar to maven/github release and then admins can just download the jar and need not checkout whole of openwhisk repo to run the admin commands. In such a case they would need to provide a custom conf

Thinking about it it now it would be fine to support that. This may require following changes

  1. Capture ArtifactStore settings as part of group vars
  2. Have a application.conf.j2 with some templating logic to render different config based on store type
  3. Have it copied to std place say ${OPENWHISK_HOME}/whisk-cli.conf or put it under ${OPENWHISK_HOME}/bin/whisk-cli.conf
  4. Have the tooling look there by default

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, thanks! Just some comments, I think these can be done later. Let me know if you need more hands on these.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. I would try to get a base setup working with some parity with current wskadmin and get that merged. Then we can collaborate on such enhancements

Copy link
Member

@rabbah rabbah Jun 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good discussion. Had similar thoughts to @tz70s and look forward to future enhancements to make it easier to use without -c.

@chetanmeh
Copy link
Member Author

@rabbah @markusthoemmes This PR is now ready for review. Currently it supports all of user and limits command.

Dumping db content efficiently across stores would require some more work and would be done in a later PR.

@chetanmeh chetanmeh added the review Review for this PR has been requested and yet needs to be done. label Jun 12, 2018
case _ =>
}
e.printStackTrace()
3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 for syntax/usage error?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. It tries to map to existing wskadmin error

class CommandError(val message: String, val code: Int)
case class IllegalState(override val message: String) extends CommandError(message, 1)
case class IllegalArg(override val message: String) extends CommandError(message, 2)

def isBlocked: Boolean = blocked.getOrElse(false)
}

private object ExtendedAuthFormat extends RootJsonFormat[WhiskAuth] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be moved to WhiskAuth?

@markusthoemmes opinion?

logging,
materializer)

class ExtendedAuth(subject: Subject, namespaces: Set[WhiskNamespace], blocked: Option[Boolean])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be moved to WhiskAuth?

@markusthoemmes opinion?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intention here is to add blocked: Option[Boolean] attribute to WhiskAuth itself?

Copy link
Member

@rabbah rabbah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the existing wskadmin tests pass against wskadmin-next?


And pass that to command via `-c` option.

$./wskadmin-next -c application-cli.conf user get guest
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as a future enhancement, making this simpler to pass a canonical conf file (ie dont have to pass it on every command) would be nice. Wether the values are defined in the environment, or there's a canonical conf file one can use.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

examples below don't show a -c file - is one required?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added them for now. Would be removed once we support a file at specific location

### Setup

Build task would have created an executable ay `bin/wskadmin-next`. This script would require config related to `ArtifactStore`
for accessing database. For e.g. to access user details from default CouchDB setup create a file `application-cli.conf`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g. -> example.


$./gradlew :tools:admin:build

This would create a jar at `tools/admin/build/libs/openwhisk-admin-tools-1.0.0-SNAPSHOT-cli.jar` and install it as an executable script at
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd replace "would create/have" etc with present tense. e.g., This creates a jar and installs it...

here and elsewhere.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done ✅

banner("OpenWhisk admin command line tool")

val verbose = tally()
val configFile = opt[File](descr = "application.conf path")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as a future enhancement, should we permit environment based overrides or using the command line to specify a configuration inline?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should work as of now also as the config loading is done in the standard way like being done when Controller/Invoker run in container

@chetanmeh
Copy link
Member Author

Can the existing wskadmin tests pass against wskadmin-next

May be not all as in some cases the message format may have changed. Would give that a try


To build the tool run

$./gradlew :tools:admin:build
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should add a redo target to build all tools (of which there is only one component now).

Build task creates an executable at `bin/wskadmin-next`. This script requires config related to `ArtifactStore`
for accessing database. For example to access user details from default CouchDB setup create a file `application-cli.conf`.

include classpath("application.conf")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should have wskadmin-next generate a template conf file as a convenience.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes thats the plan. Opened #3807 to track this

@rabbah
Copy link
Member

rabbah commented Jun 25, 2018

Suggestion:

 > ./bin/wskadmin-next 
[wskadmin] Error: Subcommand required

should instead print help.

perhaps also when given bogus arguments:

> ./bin/wskadmin-next user-h
[wskadmin] Error: Excess arguments provided: 'user-h

for contrast:

> ./bin/wskadmin-next -c `pwd`/wskadmin.conf  use list
[wskadmin] Error: Excess arguments provided: 'use list'

> wskadmin user list 
usage: wskadmin user list [-h] [-p N] [-a] [-k] namespace
wskadmin user list: error: too few arguments

i realize it may be hard to match python's argparse.

@rabbah
Copy link
Member

rabbah commented Jun 25, 2018

Should we suppress the stack trace - in this case the error message is useful enough. Perhaps a -d debug flag to emit the trace.

> ./bin/wskadmin-next user get guest
[wskadmin] Error: Incomplete config. Provide application.conf via '-c' option
pureconfig.error.ConfigReaderException: Cannot convert configuration to a whisk.core.database.CouchDbConfig. Failures are:
  at 'whisk':
    - Key not found: 'couchdb'.

	at pureconfig.package$.getResultOrThrow(package.scala:138)
	at pureconfig.package$.loadConfigOrThrow(package.scala:160)
	at whisk.core.database.CouchDbStoreProvider$.makeArtifactStore(CouchDbStoreProvider.scala:65)
	at whisk.core.database.CouchDbStoreProvider$.makeStore(CouchDbStoreProvider.scala:56)
	at whisk.core.database.UserCommand$.createDataStore(UserCommand.scala:321)
	at whisk.core.database.UserCommand.exec(UserCommand.scala:156)
	at whisk.core.cli.WhiskAdmin.executeCommand(Main.scala:159)
	at whisk.core.cli.Main$$anonfun$2.apply(Main.scala:84)
	at whisk.core.cli.Main$$anonfun$2.apply(Main.scala:83)
	at scala.util.Try$.apply(Try.scala:192)
	at whisk.core.cli.Main$.executeWithSystem(Main.scala:83)
	at whisk.core.cli.Main$.execute(Main.scala:71)
	at whisk.core.cli.Main$.main(Main.scala:64)
	at whisk.core.cli.Main.main(Main.scala)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:50)
	at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)

@rabbah
Copy link
Member

rabbah commented Jun 25, 2018

> ./bin/wskadmin-next -c wskadmin.conf  user get guest 
[wskadmin] Error: File './wskadmin.conf' not found
> ./bin/wskadmin-next -c `pwd`/wskadmin.conf  user get guest 
[...] sort of worked

@rabbah
Copy link
Member

rabbah commented Jun 25, 2018

We should probably prune these error logs.

akka.stream.StreamTcpException: Tcp command [Connect(172.17.0.1:5984,None,List(),Some(10 seconds),true)] failed because of Connection refused
[2018-06-25T18:43:43.457Z] [ERROR] [#tid_sid_cli] [CouchDbRestStore] [GET] 'whisk_local_subjects' internal error, doc: 'id: guest', failure: 'Tcp command [Connect(172.17.0.1:5984,None,List(),Some(10 seconds),true)] failed because of Connection refused' [marker:database_getDocument_error:727:109]
[ERROR] [06/25/2018 18:43:43.454] [admin-cli-dispatchers.couch-dispatcher-13] [Main$(akka://admin-cli)] [#tid_sid_cli] [CouchDbRestStore] [GET] 'whisk_local_subjects' internal error, doc: 'id: guest', failure: 'Tcp command [Connect(172.17.0.1:5984,None,List(),Some(10 seconds),true)] failed because of Connection refused' [marker:database_getDocument_error:727:109]
[ERROR] [06/25/2018 18:43:43.465] [admin-cli-akka.actor.default-dispatcher-15] [akka://admin-cli/system/pool-master] connection pool for PoolGateway(hcps = HostConnectionPoolSetup(172.17.0.1,5984,ConnectionPoolSetup(ConnectionPoolSettings(128,0,5,1024,1,30 seconds,ClientConnectionSettings(Some(User-Agent: akka-http/10.1.1),10 seconds,1 minute,512,None,WebSocketSettings(<function0>,ping,Duration.Inf,<function0>),List(),ParserSettings(2048,16,64,64,8192,64,8388608,256,52428800,Strict,RFC6265,false,Full,Error,Map(If-Range -> 0, If-Modified-Since -> 0, If-Unmodified-Since -> 0, default -> 12, Content-MD5 -> 0, Date -> 0, If-Match -> 0, If-None-Match -> 0, User-Agent -> 32),false,true,<function1>,<function1>,<function2>),None,TCPTransport),New,1 second),akka.http.scaladsl.HttpConnectionContext$@24d4a0de,akka.event.MarkerLoggingAdapter@20dd85c1))) has shut down unexpectedly

@rabbah
Copy link
Member

rabbah commented Jun 25, 2018

will we be able to add tab completion to wskadmin-next? (the wskadmin version supports tab completion if you configure the bash shell properly.)

@chetanmeh
Copy link
Member Author

Should we suppress the stack trace - in this case the error message is useful enough. Perhaps a -d debug flag to emit the trace.

Makes sense. Now only the error message would be logged. Stacktrace would be logged with -v flag. Note you can increase the log level via -vvv also

@chetanmeh
Copy link
Member Author

wskadmin] Error: File './wskadmin.conf' not found

This was happening because the Spring Boot Launch Script changes the base working directory to. one where the script file/jar is present. Fixed that now with with 6a3fc82 by looking for OLDPWD env variable.

This is done on a best-effort basis. So if OLDPWD is not found then current behaviour is used

@chetanmeh
Copy link
Member Author

We should probably prune these error logs.

Those logs were coming because Http connection pool was not getting closed. So closing them now

@chetanmeh
Copy link
Member Author

will we be able to add tab completion to wskadmin-next

Currently Scallop does not support that. Would be an interesting feature to add ... would look into this later

@chetanmeh
Copy link
Member Author

Opened #3808 wrt error messages in case of missing sub commands. This would need some changes in Scallop library used for CLI parsing. Currently some adhoc checks are done (see WhiskCommand) but yes it should be improved

@rabbah rabbah merged commit daa14c6 into apache:master Jun 26, 2018
@dubee
Copy link
Member

dubee commented Jun 26, 2018

Looks like a PG should have been ran on this one.

@chetanmeh
Copy link
Member Author

@dubee Missed on requesting the PG as changes were not impacting the runtime and local build of cli project went fine. However looks like it would impact downstream project which build main repo and not using Maven dependency

BillZong pushed a commit to BillZong/openwhisk that referenced this pull request Nov 18, 2019
Introduced wskadmin-next, an implementation of wskadmin in Scala (packaged as a fat executable) to support datastores other than CouchDB (which is the limitation of wskadmin) by sharing the implementation of the ArtifactStore.

See the documentation for how to use the new CLI. Briefly

> wskadmin-cli -c application-cli.conf user get guest

where wskadmin-cli is the executable fat jar (Gradle build would copy this to ${OPENWHISK_HOME}/bin folder) and application-cli.conf contains configuration details.

include classpath("application.conf")

whisk {
  couchdb {
    protocol = "http"
    host     = "172.17.0.1"
    port     = "5984"
    username = "whisk_admin"
    password = "some_passw0rd"
    provider = "CouchDB"
    databases {
      WhiskAuth       = "whisk_local_subjects"
      WhiskEntity     = "whisk_local_whisks"
      WhiskActivation = "whisk_local_activations"
    }
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
review Review for this PR has been requested and yet needs to be done. tooling
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants