Skip to content

Commit

Permalink
Merge pull request rundeck#7693 from rundeck/RUN-796
Browse files Browse the repository at this point in the history
RUN-796: internal project config change event includes changed keys info
  • Loading branch information
gschueler authored May 10, 2022
2 parents 0a87015 + 4a015bf commit c243fe3
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.rundeck.app.grails.events;

/**
* Defined event names used by the app within Grails events framework
*/
public class AppEvents {
public static final String PROJECT_CONFIG_CHANGED = "project.config.changed";
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import groovy.transform.CompileStatic
import groovy.transform.TypeCheckingMode
import org.apache.commons.fileupload.util.Streams
import org.rundeck.app.authorization.AppAuthContextProcessor
import org.rundeck.app.grails.events.AppEvents
import org.rundeck.app.spi.RundeckSpiBaseServicesProvider
import org.rundeck.app.spi.Services
import org.rundeck.storage.api.PathUtil
Expand Down Expand Up @@ -484,7 +485,7 @@ class ProjectManagerService implements ProjectManager, ApplicationContextAware,
}

@CompileStatic(TypeCheckingMode.SKIP)
private Map storeProjectConfig(String projectName, Properties properties) {
private Map storeProjectConfig(String projectName, Properties oldprops, Properties properties) {
def storagePath = ETC_PROJECT_PROPERTIES_PATH
def baos = new ByteArrayOutputStream()
properties['project.name']=projectName
Expand All @@ -498,10 +499,11 @@ class ProjectManagerService implements ProjectManager, ApplicationContextAware,
rundeckNodeService.refreshProjectNodes(projectName)

eventBus.notify(
'project.config.changed',
AppEvents.PROJECT_CONFIG_CHANGED,
[
project: projectName,
props : properties
project : projectName,
props : properties,
changedKeys: getKeyDiff(oldprops, properties)
]
)
return [
Expand All @@ -510,6 +512,32 @@ class ProjectManagerService implements ProjectManager, ApplicationContextAware,
creationTime: resource.contents.creationTime
]
}

/**
*
* @param orig
* @param newval
* @return set of keys which are changed, added, or removed
*/
@CompileStatic
static Set<String> getKeyDiff(Properties orig, Properties newval) {
Set<String> vals = new HashSet<>()
if(orig){
for (String key : orig.propertyNames()) {
if (newval.getProperty(key) != orig.getProperty(key)) {
vals.add(key)
}
}
for (String key : newval.propertyNames()) {
if (newval.getProperty(key) != orig.getProperty(key)) {
vals.add(key)
}
}
} else if (newval) {
vals.addAll(newval.propertyNames().collect())
}
vals
}
@CompileStatic(TypeCheckingMode.SKIP)
private void deleteProjectResources(String projectName) {
if (!deleteAllProjectFileResources(projectName)) {
Expand Down Expand Up @@ -571,7 +599,7 @@ class ProjectManagerService implements ProjectManager, ApplicationContextAware,
}
storedProps.putAll(newProps)
}
def res = storeProjectConfig(projectName, storedProps)
def res = storeProjectConfig(projectName, null, storedProps)
def rdprojectconfig = new RundeckProjectConfig(projectName,
createProjectPropertyLookup(projectName, res.config ?: new Properties()),
createDirectProjectPropertyLookup(projectName, res.config ?: new Properties()),
Expand Down Expand Up @@ -647,7 +675,7 @@ class ProjectManagerService implements ProjectManager, ApplicationContextAware,
def res = loadProjectConfigResource(projectName)
Properties oldprops = res?.config?:new Properties()
Properties newprops = mergeProperties(removePrefixes, oldprops, properties)
Map newres=storeProjectConfig(projectName, newprops)
Map newres=storeProjectConfig(projectName, oldprops, newprops)
newres
}

Expand Down Expand Up @@ -695,7 +723,8 @@ class ProjectManagerService implements ProjectManager, ApplicationContextAware,
found.save(flush: true)

}
Map resource=storeProjectConfig(projectName, properties)
def res = loadProjectConfigResource(projectName)
Map resource=storeProjectConfig(projectName, res?.config, properties)
resource
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import grails.events.bus.EventBus
import grails.test.hibernate.HibernateSpec
import grails.testing.services.ServiceUnitTest
import org.apache.commons.fileupload.util.Streams
import org.rundeck.app.grails.events.AppEvents
import org.rundeck.storage.api.PathUtil
import org.rundeck.storage.api.Resource
import org.rundeck.storage.api.StorageException
Expand Down Expand Up @@ -291,10 +292,11 @@ class ProjectManagerServiceSpec extends RundeckHibernateSpec implements ServiceU
1*service.projectCache.invalidate('test1')
1*service.rundeckNodeService.refreshProjectNodes('test1')
0*service.rundeckNodeService.getNodes('test1')
1*service.eventBus.notify('project.config.changed', {
1*service.eventBus.notify(AppEvents.PROJECT_CONFIG_CHANGED, {
it.project=='test1'
it.props.abc=='def'
it.props.'project.name'=='test1'
it.changedKeys.containsAll(it.props.keySet())
})

result.name=='test1'
Expand Down Expand Up @@ -422,6 +424,7 @@ class ProjectManagerServiceSpec extends RundeckHibernateSpec implements ServiceU
setup:
Properties props1 = new Properties()
props1['def']='ghi'
props1['abc']=abcval
new Project(name:'test1').save()
service.configStorageService=Stub(ConfigStorageService){
existsFileResource("projects/test1/etc/project.properties") >> true
Expand All @@ -437,7 +440,7 @@ class ProjectManagerServiceSpec extends RundeckHibernateSpec implements ServiceU
tprops['def']=='ghi'
},[(StorageUtil.RES_META_RUNDECK_CONTENT_TYPE):ProjectManagerService.MIME_TYPE_PROJECT_PROPERTIES]) >> Stub(Resource){
getContents()>> Stub(ResourceMeta){
getInputStream() >> new ByteArrayInputStream(('#'+ProjectManagerService.MIME_TYPE_PROJECT_PROPERTIES+'\nabc=def\ndef=ghi').bytes)
getInputStream() >> new ByteArrayInputStream(('#'+ProjectManagerService.MIME_TYPE_PROJECT_PROPERTIES+'\nabc='+abcval+'\ndef=ghi').bytes)
}
}
}
Expand All @@ -451,16 +454,22 @@ class ProjectManagerServiceSpec extends RundeckHibernateSpec implements ServiceU
then:
1*service.projectCache.invalidate('test1')
1*service.rundeckNodeService.refreshProjectNodes('test1')
1*service.eventBus.notify('project.config.changed', {
1*service.eventBus.notify(AppEvents.PROJECT_CONFIG_CHANGED, {
it.project=='test1'
it.props==[abc:'def',def:'ghi','project.name':'test1']
it.props==[abc:abcval,def:'ghi','project.name':'test1']
it.changedKeys.size()==changekeys.size()
it.changedKeys.containsAll(changekeys)
})

res != null
res.config.size() == 3
'def' == res.config['abc']
abcval == res.config['abc']
'ghi' == res.config['def']
'test1' == res.config['project.name']
where:
abcval | changekeys
'def' | ['def','project.name']
'zzz' | ['abc','def','project.name']
}

@Unroll
Expand Down Expand Up @@ -518,7 +527,7 @@ class ProjectManagerServiceSpec extends RundeckHibernateSpec implements ServiceU
existsFileResource("projects/test1/etc/project.properties") >> true
getFileResource("projects/test1/etc/project.properties") >> Stub(Resource){
getContents() >> Stub(ResourceMeta){
getInputStream() >> new ByteArrayInputStream(('#'+ProjectManagerService.MIME_TYPE_PROJECT_PROPERTIES+'\nabc=def').bytes)
getInputStream() >> new ByteArrayInputStream(('#'+ProjectManagerService.MIME_TYPE_PROJECT_PROPERTIES+'\nabc=def\nproject.name=test1').bytes)
}
}
updateFileResource("projects/test1/etc/project.properties",{InputStream inputStream->
Expand All @@ -545,9 +554,11 @@ class ProjectManagerServiceSpec extends RundeckHibernateSpec implements ServiceU

1*service.projectCache.invalidate('test1')
1*service.rundeckNodeService.refreshProjectNodes('test1')
1*service.eventBus.notify('project.config.changed', {
1*service.eventBus.notify(AppEvents.PROJECT_CONFIG_CHANGED, {
it.project=='test1'
it.props==[def:'ghi','project.name':'test1']
it.changedKeys.size()==2
it.changedKeys.containsAll(['abc', 'def'])
})
res!=null
res.config.size()==2
Expand All @@ -562,7 +573,12 @@ class ProjectManagerServiceSpec extends RundeckHibernateSpec implements ServiceU
props1['project.name']='not-right'
new Project(name:'test1').save(flush: true)
service.configStorageService=Mock(ConfigStorageService){
1 * existsFileResource("projects/test1/etc/project.properties") >> true
_ * existsFileResource("projects/test1/etc/project.properties") >> true
1 * getFileResource("projects/test1/etc/project.properties") >> Stub(Resource){
getContents() >> Stub(ResourceMeta){
getInputStream() >> new ByteArrayInputStream(('#'+ProjectManagerService.MIME_TYPE_PROJECT_PROPERTIES+'\na=b').bytes)
}
}
1 * updateFileResource("projects/test1/etc/project.properties",{ins->
def tprops=new Properties()
tprops.load(ins)
Expand Down Expand Up @@ -1091,4 +1107,24 @@ class ProjectManagerServiceSpec extends RundeckHibernateSpec implements ServiceU
'a description' | 'a description'
null | null
}

def "getKeyDiff"() {
given:
Properties orig = new Properties()
orig.putAll([a: 'aaa', b: 'bbb'])
Properties newvals = new Properties()
newvals.putAll(newprops)
when:
def result = ProjectManagerService.getKeyDiff(orig, newvals)
then:
result == expected.toSet()
where:
newprops | expected
[a: 'aaa', b: 'bbb'] | []
[a: 'XXX', b: 'bbb'] | ['a']
[b: 'bbb'] | ['a']
[a: 'aaa', b: 'XXX'] | ['b']
[a: 'aaa'] | ['b']
[:] | ['a', 'b']
}
}

0 comments on commit c243fe3

Please sign in to comment.