openhab-core icon indicating copy to clipboard operation
openhab-core copied to clipboard

[updater] A component for self-updating openHAB via the UI

Open andrewfg opened this issue 4 years ago • 33 comments

This is a first attempt at creating a core module which supports self- updating of OpenHAB via the UI.

This PR is made available to allow OpenHAB maintainers to review the code and give feedback.

It is only a proof of concept (or not). It is not yet intended to be merged at this time.

Instructions: build the jar, drop it in /addons, and then go to Developer Tools | Api Explorer | Update

Signed-off-by: Andrew Fiddian-Green [email protected]

andrewfg avatar May 19 '21 15:05 andrewfg

This pull request has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/addon-to-update-oh-thoughts-anyone/121576/11

openhab-bot avatar May 19 '21 15:05 openhab-bot

Dear All, The first reviewers focussed on the UI of this module. There is nothing wrong with that, but IMHO the real focus should be on the functionality rather than the UI. In particular I would suggest (indeed request) some feedback on the following items..

  • The BaseUpdater class, in particular the way it builds the update scripts and injects them into the operating system shell.
  • The classes that extend from BaseUpdater for specific OS; the WindowsUpdater and DebianUpdater are tested, but the others (Mac, Yum, PacMan, Portage, etc.) need someone who uses those systems.
  • The update script templates associated with the respective OS updaters DebianUpdater.txt, WindowsXX.txt etc. etc.

andrewfg avatar May 29 '21 10:05 andrewfg

Hi @andrewfg,

I just had a closer look in order to provide some feedback.

  • I see that you have put a lot of work into a UI, which is actually pretty unexpected for me here. When I suggested moving the feature to openHAB core as part of the REST API, I meant exactly this: Make the feature available through the REST API and nothing more. Naturally, the Main UI can then be enhanced to consume this new REST resource, just as it does for all the others. This especially make sense if you spin the idea further: The REST resource could provide info on whether a new update is available and the Main UI could present such infos to the user. It simply does not make sense to have that as a separate servlet where the user has to log in independently. And specifically, UIs do not belong into openhab-core. My suggestion is therefore to remove the UI parts and focus on the REST resource in this PR.
  • The right place to add new REST resources is in a subpackage of org.openhab.core.io.rest. As a bundle name, I'd hence suggest to use org.openhab.core.io.rest.update.
  • The resource should definitely only be made available for Role.ADMIN, but not for Role.USER
  • All classes should be in an internal package as I do not see the need to export any of them from the bundle.
  • Please fix the spelling from OpenHab or (OpenHAB) to openHAB throughout your code.

Once you have refactored to this point, I'll be happy to do an in-depth review.

kaikreuzer avatar Jun 06 '21 16:06 kaikreuzer

@kaikreuzer many thanks for the feedback. I will move/rename the package as you suggest. There is only one Java class which is UI related - namely the ServletUI class (component); as there is currently no way to really test things without some contribution from others on the UI side, I suggest to leave that class present for the time being. Or?? perhaps move it to "test"...

andrewfg avatar Jun 06 '21 16:06 andrewfg

The REST resource could provide info on whether a new update is available

PS it does that already..

andrewfg avatar Jun 06 '21 17:06 andrewfg

there is currently no way to really test things without some contribution from others on the UI side

Can't it be easily tested by using the developer tools of the Main UI? This makes it imho pretty simple to test REST resources and that should be just fine for testing the feature.

kaikreuzer avatar Jun 06 '21 18:06 kaikreuzer

Remove the UI parts and focus on the REST resource in this PR.

Ok. Done.

The right place is in a subpackage of org.openhab.core.io.rest .. org.openhab.core.io.rest.update.

Ok. Done.

The resource should definitely only be made available for Role.ADMIN, but not for Role.USER

Not entirely. There is a REST GET which allows both USER's and ADMIN's to see if there is an update available. And a REST POST which allows only ADMIN's to execute an update.

All classes should be in an internal package

Ok. Done.

Please fix the spelling from OpenHab or (OpenHAB) to openHAB

Ok. Done. Exception: Java class and field names that must be camel case..

andrewfg avatar Jun 07 '21 15:06 andrewfg

image

andrewfg avatar Jun 07 '21 16:06 andrewfg

I'll have to connect with the debugger in order to see what is happening

Not sure if a debugger will help you much here. The Java code is creating and starting a shell script that stops OH, does the update, and then re starts OH again. So the Java debugger has no way to see what may be going wrong in that shell script.

EDIT depending on OS, the shell script file is called ‘self-updater.sh’ (or .cmd) and shell Stdout / Stderr may be written to ‘self-updater.log’ (or .txt). Normally these ‘self-updater.*’ files will be auto deleted when OH restarts; but if the log level is DEBUG those files will persist across restarts, so you can see what happened..

andrewfg avatar Jun 08 '21 07:06 andrewfg

Thanks for your updates, @andrewfg! Just to manage expectations: This won't make it into 3.1 as we will need more testing and the according support in the Main UI. I therefore will focus the next days on stuff that is relevant for 3.1 and come back to this PR right after that. So please excuse me being silent for a little while, you are not forgotten!

kaikreuzer avatar Jun 15 '21 20:06 kaikreuzer

please excuse me being silent for a little while

@kaikreuzer no problem; as I have only Windows and Debian, we will anyway need people to contribute for the other OS parts..

andrewfg avatar Jun 15 '21 22:06 andrewfg

@kaikreuzer just pushed the 3.1.0.RC1 version today, and since this is a rare occurrence, below is (just for the record) the JSON response reported by the updater just now.

It compares the actual running version 3.1.0.M5 against the potential future version "3.1.0.RC1" and judges the latter to be a valid update for the MILESTONE branch i.e. it returns newVersionAvailable = true -- which is correct.

EDIT: btw the update process (Pi4 / Debian) worked too :)

{
  "actualVersion": {
    "versionName": "3.1.0.M5",
    "versionType": "MILESTONE"
  },
  "latestVersionCount": 3,
  "latestVersions": [
    {
      "versionName": "3.1.0",
      "versionType": "STABLE"
    },
    {
      "versionName": "3.1.0.RC1",
      "versionType": "MILESTONE"
    },
    {
      "versionName": "3.2.0.202106272039",
      "versionType": "SNAPSHOT"
    }
  ],
  "targetNewVersionType": "MILESTONE",
  "newVersionAvailable": true
}

andrewfg avatar Jun 28 '21 13:06 andrewfg

Trying to test the current version of the PR, I got a NPE directly at startup:

22:06:33.265 [ERROR] [rnal.restresource.UpdaterRestResource] - bundle org.openhab.core.io.rest.update:3.2.0.202107012002 (235)[org.openhab.core.io.rest.update.internal.restresource.UpdaterRestResource(7)] : Error during instantiation of the implementation object
java.lang.reflect.InvocationTargetException: null
	at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:?]
	at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[?:?]
	at jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:?]
	at java.lang.reflect.Constructor.newInstance(Constructor.java:490) ~[?:?]
	at org.apache.felix.scr.impl.inject.internal.ComponentConstructorImpl.newInstance(ComponentConstructorImpl.java:316) ~[?:?]
	at org.apache.felix.scr.impl.manager.SingleComponentManager.createImplementationObject(SingleComponentManager.java:286) ~[?:?]
	at org.apache.felix.scr.impl.manager.SingleComponentManager.createComponent(SingleComponentManager.java:115) ~[?:?]
	at org.apache.felix.scr.impl.manager.SingleComponentManager.getService(SingleComponentManager.java:1000) ~[?:?]
	at org.apache.felix.scr.impl.manager.SingleComponentManager.getServiceInternal(SingleComponentManager.java:973) ~[?:?]
	at org.apache.felix.scr.impl.manager.SingleComponentManager.getService(SingleComponentManager.java:918) ~[?:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceFactoryUse$1.run(ServiceFactoryUse.java:216) ~[org.eclipse.osgi-3.16.200.jar:?]
	at java.security.AccessController.doPrivileged(Native Method) ~[?:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceFactoryUse.factoryGetService(ServiceFactoryUse.java:213) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceFactoryUse.getService(ServiceFactoryUse.java:114) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceUse.newServiceObject(ServiceUse.java:99) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceConsumer$1.getService(ServiceConsumer.java:33) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistrationImpl.getService(ServiceRegistrationImpl.java:547) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceObjectsImpl.getService(ServiceObjectsImpl.java:92) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.apache.aries.jax.rs.whiteboard.internal.utils.Utils.lambda$null$7(Utils.java:206) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.Publisher.apply(Publisher.java:28) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$75(OSGi.java:715) ~[?:?]
	at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$recoverWith$76(OSGi.java:711) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.internal.BundleContextOSGiImpl.lambda$new$0(BundleContextOSGiImpl.java:29) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.internal.ChangeContextOSGiImpl.lambda$new$0(ChangeContextOSGiImpl.java:31) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$recoverWith$76(OSGi.java:711) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
	at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
	at org.apache.aries.component.dsl.Publisher.apply(Publisher.java:28) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$29(OSGi.java:296) ~[?:?]
	at org.apache.aries.component.dsl.internal.UpdateSupport.deferPublication(UpdateSupport.java:40) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$32(OSGi.java:295) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
	at org.apache.aries.jax.rs.whiteboard.internal.ServiceReferenceFilteredPublisher.publishIfMatched(ServiceReferenceFilteredPublisher.java:55) ~[?:?]
	at org.apache.aries.jax.rs.whiteboard.internal.ServiceReferenceRegistry.register(ServiceReferenceRegistry.java:92) ~[?:?]
	at org.apache.aries.jax.rs.whiteboard.internal.cxf.CxfJaxrsServiceRegistrator.registerExtension(CxfJaxrsServiceRegistrator.java:327) ~[?:?]
	at org.apache.aries.jax.rs.whiteboard.internal.Whiteboard.lambda$null$63(Whiteboard.java:962) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:599) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
	at org.apache.aries.component.dsl.Publisher.apply(Publisher.java:28) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$75(OSGi.java:715) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
	at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.Publisher.apply(Publisher.java:28) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$75(OSGi.java:715) ~[?:?]
	at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$recoverWith$76(OSGi.java:711) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.internal.BundleContextOSGiImpl.lambda$new$0(BundleContextOSGiImpl.java:29) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.internal.ChangeContextOSGiImpl.lambda$new$0(ChangeContextOSGiImpl.java:31) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$recoverWith$76(OSGi.java:711) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
	at org.apache.aries.jax.rs.whiteboard.internal.FilteredPublisher.publishIfMatched(FilteredPublisher.java:51) ~[?:?]
	at org.apache.aries.jax.rs.whiteboard.internal.Registry.lambda$waitForService$1(Registry.java:69) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.internal.EffectsOSGi.lambda$new$1(EffectsOSGi.java:35) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
	at org.apache.aries.component.dsl.Publisher.apply(Publisher.java:28) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$66(OSGi.java:663) ~[?:?]
	at org.apache.aries.component.dsl.internal.Pad.publish(Pad.java:59) ~[?:?]
	at org.apache.aries.component.dsl.internal.DistributeOSGiImpl.lambda$null$1(DistributeOSGiImpl.java:51) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
	at org.apache.aries.component.dsl.Publisher.apply(Publisher.java:28) ~[?:?]
	at org.apache.aries.component.dsl.internal.HighestRankingOSGi.lambda$null$1(HighestRankingOSGi.java:62) ~[?:?]
	at org.apache.aries.component.dsl.internal.Pad.publish(Pad.java:59) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$79(OSGi.java:739) ~[?:?]
	at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
	at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$80(OSGi.java:732) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
	at org.apache.aries.component.dsl.Publisher.apply(Publisher.java:28) ~[?:?]
	at org.apache.aries.component.dsl.OSGi.lambda$null$66(OSGi.java:663) ~[?:?]
	at org.apache.aries.component.dsl.Publisher.apply(Publisher.java:28) ~[?:?]
	at org.apache.aries.component.dsl.internal.ServiceReferenceOSGi$DefaultServiceTrackerCustomizer.addingService(ServiceReferenceOSGi.java:74) ~[?:?]
	at org.apache.aries.component.dsl.internal.ServiceReferenceOSGi$DefaultServiceTrackerCustomizer.addingService(ServiceReferenceOSGi.java:56) ~[?:?]
	at org.osgi.util.tracker.ServiceTracker$Tracked.customizerAdding(ServiceTracker.java:943) ~[osgi.core-7.0.0.jar:?]
	at org.osgi.util.tracker.ServiceTracker$Tracked.customizerAdding(ServiceTracker.java:871) ~[osgi.core-7.0.0.jar:?]
	at org.osgi.util.tracker.AbstractTracked.trackAdding(AbstractTracked.java:256) ~[osgi.core-7.0.0.jar:?]
	at org.osgi.util.tracker.AbstractTracked.track(AbstractTracked.java:229) ~[osgi.core-7.0.0.jar:?]
	at org.osgi.util.tracker.ServiceTracker$Tracked.serviceChanged(ServiceTracker.java:903) ~[osgi.core-7.0.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.FilteredServiceListener.serviceChanged(FilteredServiceListener.java:120) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.internal.framework.BundleContextImpl.dispatchEvent(BundleContextImpl.java:957) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:234) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.publishServiceEventPrivileged(ServiceRegistry.java:936) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.publishServiceEvent(ServiceRegistry.java:873) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistrationImpl.register(ServiceRegistrationImpl.java:141) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.registerService(ServiceRegistry.java:261) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.internal.framework.BundleContextImpl.registerService(BundleContextImpl.java:496) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager$3.register(AbstractComponentManager.java:929) ~[?:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager$3.register(AbstractComponentManager.java:915) ~[?:?]
	at org.apache.felix.scr.impl.manager.RegistrationManager.changeRegistration(RegistrationManager.java:133) ~[?:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.registerService(AbstractComponentManager.java:984) ~[?:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.activateInternal(AbstractComponentManager.java:752) ~[?:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.enableInternal(AbstractComponentManager.java:674) ~[?:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.enable(AbstractComponentManager.java:437) ~[?:?]
	at org.apache.felix.scr.impl.manager.ConfigurableComponentHolder.enableComponents(ConfigurableComponentHolder.java:667) ~[?:?]
	at org.apache.felix.scr.impl.BundleComponentActivator.initialEnable(BundleComponentActivator.java:305) ~[?:?]
	at org.apache.felix.scr.impl.Activator.loadComponents(Activator.java:554) ~[?:?]
	at org.apache.felix.scr.impl.Activator.access$200(Activator.java:70) ~[?:?]
	at org.apache.felix.scr.impl.Activator$ScrExtension.start(Activator.java:421) ~[?:?]
	at org.apache.felix.scr.impl.AbstractExtender.createExtension(AbstractExtender.java:196) ~[?:?]
	at org.apache.felix.scr.impl.AbstractExtender.modifiedBundle(AbstractExtender.java:169) ~[?:?]
	at org.apache.felix.scr.impl.AbstractExtender.modifiedBundle(AbstractExtender.java:49) ~[?:?]
	at org.osgi.util.tracker.BundleTracker$Tracked.customizerModified(BundleTracker.java:488) ~[osgi.core-7.0.0.jar:?]
	at org.osgi.util.tracker.BundleTracker$Tracked.customizerModified(BundleTracker.java:420) ~[osgi.core-7.0.0.jar:?]
	at org.osgi.util.tracker.AbstractTracked.track(AbstractTracked.java:232) ~[osgi.core-7.0.0.jar:?]
	at org.osgi.util.tracker.BundleTracker$Tracked.bundleChanged(BundleTracker.java:450) ~[osgi.core-7.0.0.jar:?]
	at org.eclipse.osgi.internal.framework.BundleContextImpl.dispatchEvent(BundleContextImpl.java:945) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:234) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.internal.framework.EquinoxEventPublisher.publishBundleEventPrivileged(EquinoxEventPublisher.java:232) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.internal.framework.EquinoxEventPublisher.publishBundleEvent(EquinoxEventPublisher.java:138) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.internal.framework.EquinoxEventPublisher.publishBundleEvent(EquinoxEventPublisher.java:130) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.internal.framework.EquinoxContainerAdaptor.publishModuleEvent(EquinoxContainerAdaptor.java:217) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.container.Module.publishEvent(Module.java:499) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.container.Module.start(Module.java:486) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:440) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:459) ~[org.eclipse.osgi-3.16.200.jar:?]
	at org.apache.karaf.features.internal.service.BundleInstallSupportImpl.startBundle(BundleInstallSupportImpl.java:165) ~[?:?]
	at org.apache.karaf.features.internal.service.FeaturesServiceImpl.startBundle(FeaturesServiceImpl.java:1160) ~[?:?]
	at org.apache.karaf.features.internal.service.Deployer.deploy(Deployer.java:1041) ~[?:?]
	at org.apache.karaf.features.internal.service.FeaturesServiceImpl.doProvision(FeaturesServiceImpl.java:1069) ~[?:?]
	at org.apache.karaf.features.internal.service.FeaturesServiceImpl.lambda$doProvisionInThread$13(FeaturesServiceImpl.java:1004) ~[?:?]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
	at java.lang.Thread.run(Thread.java:834) [?:?]
Caused by: java.lang.NullPointerException
	at org.openhab.core.io.rest.update.internal.updaters.MacUpdater.initializeExtendedPlaceholders(MacUpdater.java:46) ~[?:?]
	at org.openhab.core.io.rest.update.internal.updaters.BaseUpdater.<init>(BaseUpdater.java:151) ~[?:?]
	at org.openhab.core.io.rest.update.internal.updaters.MacUpdater.<init>(MacUpdater.java:26) ~[?:?]
	at org.openhab.core.io.rest.update.internal.factory.UpdaterFactory.newUpdater(UpdaterFactory.java:95) ~[?:?]
	at org.openhab.core.io.rest.update.internal.restresource.UpdaterRestResource.<init>(UpdaterRestResource.java:76) ~[?:?]
	... 236 more
22:06:33.320 [WARN ] [ard.internal.AriesJaxrsServiceRuntime] - Resource from reference CachingServiceReference {
cachedProperties={osgi.jaxrs.application.select=(osgi.jaxrs.name=openhab), osgi.jaxrs.name=update, osgi.jaxrs.extension.select=(osgi.jaxrs.media.type=application/json), osgi.jaxrs.whiteboard.target=null (cached)}
serviceReference={org.openhab.core.io.rest.RESTResource}={osgi.jaxrs.resource=true, service.id=144, service.bundleid=235, service.scope=bundle, osgi.jaxrs.application.select=(osgi.jaxrs.name=openhab), osgi.jaxrs.name=update, osgi.jaxrs.extension.select=(osgi.jaxrs.media.type=application/json), component.name=org.openhab.core.io.rest.update.internal.restresource.UpdaterRestResource, component.id=7}
} can't be got

kaikreuzer avatar Jul 01 '21 20:07 kaikreuzer

Oops. Sorry about the NPE in the MacUpdater (as mentioned, I don't have a Mac, do that code was untested). I am taking a week's vacation, so I will get back to you with a fix in about 10 days time.

andrewfg avatar Jul 02 '21 16:07 andrewfg

org.openhab.core.io.rest.update.internal.updaters.MacUpdater.initializeExtendedPlaceholders(MacUpdater.java:46)

Hmm. It looks like logger is still null at line 46; which is not expected..

andrewfg avatar Jul 02 '21 16:07 andrewfg

@kaikreuzer I have made some changes based on your recent feedback. However unfortunately by changing the version from 3.1.0-SNAPSHOT to 3.2.0-SNAPSHOT this PR seems to have "acquired" the 370 or so files that changed when 3.1.0 was released. It is now a mess, and I don't know how to fix that.

andrewfg avatar Jul 11 '21 16:07 andrewfg

unfortunately by changing the version from 3.1.0-SNAPSHOT to 3.2.0-SNAPSHOT this PR seems to have "acquired" the 370 or so files that changed when 3.1.0 was released

@kaikreuzer the above-mentioned issue has now been resolved :)

andrewfg avatar Jul 20 '21 16:07 andrewfg

https://github.com/openhab/openhab-core/pull/2372#issuecomment-872517867

@kaikreuzer resolved

andrewfg avatar Jul 20 '21 16:07 andrewfg

@kaikreuzer => ping!

andrewfg avatar Aug 17 '21 13:08 andrewfg

Just to report that this did successfully install 3.2.M2 today (see screenshot below)..

image

andrewfg avatar Sep 06 '21 16:09 andrewfg

Cool! Will look at it soon®, promised, @andrewfg!

kaikreuzer avatar Sep 06 '21 18:09 kaikreuzer

This pull request has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/validation-api-usage/126439/1

openhab-bot avatar Sep 12 '21 18:09 openhab-bot

Hey @andrewfg! Very sorry that it took so long, but finally I now had a closer look at it and also tested it on macOS. The code of the PR looks pretty clean, thanks for the good job on it!

Unfortunately, I didn't manage to get it working on macOS. I made a couple of fixes (like also setting the executable flag on the script, calling it as the provided user and not as root, etc.), but the execution of the single steps always fails as the commands expect to be run in some other folder. Manually calling it during my tests it behaved differently than when being called from the JVM and I couldn't really figure out how to fix it.

Thinking about it, I also wasn't sure how the script should actually know, how to correctly start the instance. Some people run it through "bin/karaf start", others might use "bin/start", I am running it within screen, others might do nohup or special service wrappers. I somehow doubt that there's a solution that will just work and not be very fragile. I have the same concerns for Windows users - the current script simply starts the instance, but what if people run it as a Windows service as described here? On Linux, the code assumes that openHAB is installed through the package manager of the distro. But what if it is manually installed or used as a snap in a docker container?

The more I think about it, the less convinced I am that we can get this a stable feature. Happy to discuss how to proceed - also inviting the other @openhab/core-maintainers.

kaikreuzer avatar Oct 25 '21 19:10 kaikreuzer

The more I think about it, the less convinced I am that we can get this a stable feature.

We might just only want to provide APIs here in core, so the UI knows if an update mechanism is available, and what parameters are used (password, available versions). Then the actual update script(s) and files that supply the parameters can move to distro projects. It would also allow users to have a way to plug-in their own update mechanism if they use something of their own. The available versions also depends on what installation method is used. E.g. versions aren't always immediately available for all platforms/installation methods (Synology, snap, etc).

wborn avatar Oct 27 '21 07:10 wborn

might just only want to provide APIs here in core, so the UI knows if an update mechanism is available, and what parameters are used (password, available versions). Then the actual update script(s) and files that supply the parameters can move to distro projects.

This is essentially what I did already. There is a main API with a REST GET to read the current update state, and a POST to start the update process. And then for each type of installation / distro there are just two distro specific elements — namely i) a small Java class that extends/ overrides some methods in the main updater class, and ii) a text file that contains the update command script.

I have only written these two elements for OpenHabian an Windows so far..

andrewfg avatar Oct 27 '21 08:10 andrewfg

IMHO the core should not know anything about package specific upgrade commands. It should only know that there is a script (or class) that can do this and what kind of parameters it expects that can then be provided using the UI. I.e. it uses some abstraction where the package adds a file or property to indicate it can do the update using what parameters. Then the actual upgrade scripts move to either openhab-distro, openhab-linuxpkg, openhab-syno-spk, openhab-snap etc where they can be maintained and which already have all the package specific implementation details.

wborn avatar Oct 27 '21 10:10 wborn

the core should not know anything about package specific upgrade commands. It should only know that there is a script (or class) that can do this and what kind of parameters it expects that can then be provided using the UI

Indeed. (That is what I am doing). The scripts contain placeholders for parameters like target version, target version type, password etc. and the core replaces the placeholders in the scripts.

andrewfg avatar Oct 27 '21 11:10 andrewfg

This pull request has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/upgrading-system-via-gui/138453/13

openhab-bot avatar Aug 27 '22 12:08 openhab-bot

@andrewfg What is the state here?

J-N-K avatar May 14 '23 12:05 J-N-K

What is the state here?

I think it is 'sleeping' .. @kaikreuzer / @wborn WDYT? .. if you agree we can close it, or make it WIP ..

andrewfg avatar May 14 '23 16:05 andrewfg