<commit_before>/*
Copyright 2019 The Rook Authors. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
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.
*/

package installer

const (
	rbdProvisionerTemplate = `
    kind: StatefulSet
    apiVersion: apps/v1
    metadata:
      name: csi-rbdplugin-provisioner
      namespace: {{ .Namespace }}
    spec:
      serviceName: "csi-rbdplugin-provisioner"
      replicas: 1
      selector:
        matchLabels:
         app: csi-rbdplugin-provisioner
      template:
        metadata:
          labels:
            app: csi-rbdplugin-provisioner
        spec:
          serviceAccount: rook-csi-rbd-provisioner-sa
          containers:
            - name: csi-provisioner
              image: {{ .ProvisionerImage }}
              args:
                - "--csi-address=$(ADDRESS)"
                - "--v=5"
                - "--timeout=60s"
                - "--retry-interval-start=500ms"
              env:
                - name: ADDRESS
                  value: unix:///csi/csi-provisioner.sock
              imagePullPolicy: "IfNotPresent"
              volumeMounts:
                - name: socket-dir
                  mountPath: /csi
            - name: csi-rbdplugin-attacher
              image: {{ .AttacherImage }}
              args:
                - "--v=5"
                - "--csi-address=$(ADDRESS)"
              env:
                - name: ADDRESS
                  value: /csi/csi-provisioner.sock
              imagePullPolicy: "IfNotPresent"
              volumeMounts:
                - name: socket-dir
                  mountPath: /csi

            - name: csi-snapshotter
              image:  {{ .SnapshotterImage }}
              args:
                - "--csi-address=$(ADDRESS)"
                - "--v=5"
                - "--timeout=60s"
              env:
                - name: ADDRESS
                  value: unix:///csi/csi-provisioner.sock
              imagePullPolicy: Always
              securityContext:
                privileged: true
              volumeMounts:
                - name: socket-dir
                  mountPath: /csi
            - name: csi-rbdplugin
              securityContext:
                privileged: true
                capabilities:
                  add: ["SYS_ADMIN"]
              image: {{ .CSIPluginImage }}
              args :
                - "--nodeid=$(NODE_ID)"
                - "--endpoint=$(CSI_ENDPOINT)"
                - "--v=5"
                - "--type=rbd"
                - "--drivername={{ .DriverNamePrefix }}rbd.csi.ceph.com"
                - "--containerized=true"
                - "--pidlimit=-1"
              env:
                - name: HOST_ROOTFS
                  value: "/rootfs"
                - name: NODE_ID
                  valueFrom:
                    fieldRef:
                      fieldPath: spec.nodeName
                - name: POD_NAMESPACE
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.namespace
                - name: CSI_ENDPOINT
                  value: unix:///csi/csi-provisioner.sock
              imagePullPolicy: "IfNotPresent"
              volumeMounts:
                - name: socket-dir
                  mountPath: /csi
                - mountPath: /dev
                  name: host-dev
                - mountPath: /rootfs
                  name: host-rootfs
                - mountPath: /sys
                  name: host-sys
                - mountPath: /lib/modules
                  name: lib-modules
                  readOnly: true
                - name: ceph-csi-config
                  mountPath: /etc/ceph-csi-config/
                - name: keys-tmp-dir
                  mountPath: /tmp/csi/keys
          volumes:
            - name: host-dev
              hostPath:
                path: /dev
            - name: host-rootfs
              hostPath:
                path: /
            - name: host-sys
              hostPath:
                path: /sys
            - name: lib-modules
              hostPath:
                path: /lib/modules
            - name: socket-dir
              hostPath:
                path: /var/lib/kubelet/plugins/{{ .DriverNamePrefix }}rbd.csi.ceph.com
                type: DirectoryOrCreate
            - name: ceph-csi-config
              configMap:
                name: rook-ceph-csi-config
                items:
                  - key: csi-cluster-config-json
                    path: config.json
            - name: keys-tmp-dir
              emptyDir: {
                medium: "Memory"
              }
`
	rbdPluginTemplate = `
    kind: DaemonSet
    apiVersion: apps/v1
    metadata:
      name: csi-rbdplugin
      namespace: {{ .Namespace }}
    spec:
      selector:
        matchLabels:
          app: csi-rbdplugin
      template:
        metadata:
          labels:
            app: csi-rbdplugin
        spec:
          serviceAccount: rook-csi-rbd-plugin-sa
          hostNetwork: true
          hostPID: true
          # to use e.g. Rook orchestrated cluster, and mons' FQDN is
          # resolved through k8s service, set dns policy to cluster first
          dnsPolicy: ClusterFirstWithHostNet
          containers:
            - name: driver-registrar
              image: {{ .RegistrarImage }}
              args:
                - "--v=5"
                - "--csi-address=/csi/csi.sock"
                - "--kubelet-registration-path=/var/lib/kubelet/plugins/{{ .DriverNamePrefix }}rbd.csi.ceph.com/csi.sock"
              lifecycle:
                preStop:
                  exec:
                      command: ["/bin/sh", "-c", "rm -rf /registration/csi-rbdplugin /registration/csi-rbdplugin-reg.sock"]
              env:
                - name: KUBE_NODE_NAME
                  valueFrom:
                    fieldRef:
                      fieldPath: spec.nodeName
              volumeMounts:
                - name: plugin-dir
                  mountPath: /csi
                - name: registration-dir
                  mountPath: /registration
            - name: csi-rbdplugin
              securityContext:
                privileged: true
                capabilities:
                  add: ["SYS_ADMIN"]
                allowPrivilegeEscalation: true
              image: {{ .CSIPluginImage }}
              args :
                - "--nodeid=$(NODE_ID)"
                - "--endpoint=$(CSI_ENDPOINT)"
                - "--v=5"
                - "--type=rbd"
                - "--drivername={{ .DriverNamePrefix }}rbd.csi.ceph.com"
                - "--containerized=true"
              env:
                - name: HOST_ROOTFS
                  value: "/rootfs"
                - name: NODE_ID
                  valueFrom:
                    fieldRef:
                      fieldPath: spec.nodeName
                - name: POD_NAMESPACE
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.namespace
                - name: CSI_ENDPOINT
                  value: unix:///csi/csi.sock
              imagePullPolicy: "IfNotPresent"
              volumeMounts:
                - name: plugin-dir
                  mountPath: /csi
                - name: pods-mount-dir
                  mountPath: /var/lib/kubelet/pods
                  mountPropagation: "Bidirectional"
                - name: plugin-mount-dir
                  mountPath: /var/lib/kubelet/plugins
                  mountPropagation: "Bidirectional"
                - mountPath: /dev
                  name: host-dev
                - mountPath: /rootfs
                  name: host-rootfs
                - mountPath: /sys
                  name: host-sys
                - mountPath: /lib/modules
                  name: lib-modules
                  readOnly: true
                - name: ceph-csi-config
                  mountPath: /etc/ceph-csi-config/
                - name: keys-tmp-dir
                  mountPath: /tmp/csi/keys
                - name: host-run-mount
                  mountPath: /run/mount
          volumes:
            - name: plugin-dir
              hostPath:
                path: /var/lib/kubelet/plugins/{{ .DriverNamePrefix }}rbd.csi.ceph.com
                type: DirectoryOrCreate
            - name: plugin-mount-dir
              hostPath:
                path: /var/lib/kubelet/plugins
                type: Directory
            - name: registration-dir
              hostPath:
                path: /var/lib/kubelet/plugins_registry/
                type: Directory
            - name: pods-mount-dir
              hostPath:
                path: /var/lib/kubelet/pods
                type: Directory
            - name: host-dev
              hostPath:
                path: /dev
            - name: host-rootfs
              hostPath:
                path: /
            - name: host-sys
              hostPath:
                path: /sys
            - name: lib-modules
              hostPath:
                path: /lib/modules
            - name: ceph-csi-config
              configMap:
                name: rook-ceph-csi-config
                items:
                  - key: csi-cluster-config-json
                    path: config.json
            - name: keys-tmp-dir
              emptyDir: {
                medium: "Memory"
              }
            - name: host-run-mount
              hostPath:
                path: /run/mount
    `
	cephfsProvisionerTemplate = `
    kind: StatefulSet
    apiVersion: apps/v1
    metadata:
      name: csi-cephfsplugin-provisioner
      namespace: {{ .Namespace }}
    spec:
      serviceName: "csi-cephfsplugin-provisioner"
      replicas: 1
      selector:
        matchLabels:
         app: csi-cephfsplugin-provisioner
      template:
        metadata:
          labels:
            app: csi-cephfsplugin-provisioner
        spec:
          serviceAccount: rook-csi-cephfs-provisioner-sa
          containers:
            - name: csi-provisioner
              image: {{ .ProvisionerImage }}
              args:
                - "--csi-address=$(ADDRESS)"
                - "--v=5"
                - "--timeout=60s"
                - "--retry-interval-start=500ms"
              env:
                - name: ADDRESS
                  value: unix:///csi/csi-provisioner.sock
              imagePullPolicy: "IfNotPresent"
              volumeMounts:
                - name: socket-dir
                  mountPath: /csi
            - name: csi-attacher
              image: {{ .AttacherImage }}
              args:
                - "--v=5"
                - "--csi-address=$(ADDRESS)"
              env:
                - name: ADDRESS
                  value: /csi/csi-provisioner.sock
              imagePullPolicy: "IfNotPresent"
              volumeMounts:
                - name: socket-dir
                  mountPath: /csi
            - name: csi-cephfsplugin
              securityContext:
                privileged: true
                capabilities:
                  add: ["SYS_ADMIN"]
              image: {{ .CSIPluginImage }}
              args :
                - "--nodeid=$(NODE_ID)"
                - "--endpoint=$(CSI_ENDPOINT)"
                - "--v=5"
                - "--type=cephfs"
                - "--drivername={{ .DriverNamePrefix }}cephfs.csi.ceph.com"
                - "--pidlimit=-1"
              env:
                - name: NODE_ID
                  valueFrom:
                    fieldRef:
                      fieldPath: spec.nodeName
                - name: POD_NAMESPACE
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.namespace
                - name: CSI_ENDPOINT
                  value: unix:///csi/csi-provisioner.sock
              imagePullPolicy: "IfNotPresent"
              volumeMounts:
                - name: socket-dir
                  mountPath: /csi
                - name: host-sys
                  mountPath: /sys
                - name: lib-modules
                  mountPath: /lib/modules
                  readOnly: true
                - name: host-dev
                  mountPath: /dev
                - name: ceph-csi-config
                  mountPath: /etc/ceph-csi-config/
                - name: keys-tmp-dir
                  mountPath: /tmp/csi/keys
          volumes:
            - name: socket-dir
              hostPath:
                path: /var/lib/kubelet/plugins/{{ .DriverNamePrefix }}cephfs.csi.ceph.com
                type: DirectoryOrCreate
            - name: host-sys
              hostPath:
                path: /sys
            - name: lib-modules
              hostPath:
                path: /lib/modules
            - name: host-dev
              hostPath:
                path: /dev
            - name: ceph-csi-config
              configMap:
                name: rook-ceph-csi-config
                items:
                  - key: csi-cluster-config-json
                    path: config.json
            - name: keys-tmp-dir
              emptyDir: {
                medium: "Memory"
              }
`
	cephfsPluginTemplate = `
    kind: DaemonSet
    apiVersion: apps/v1
    metadata:
      name: csi-cephfsplugin
      namespace: {{ .Namespace }}
    spec:
      selector:
        matchLabels:
          app: csi-cephfsplugin
      template:
        metadata:
          labels:
            app: csi-cephfsplugin
        spec:
          serviceAccount: rook-csi-cephfs-plugin-sa
          hostNetwork: true
          # to use e.g. Rook orchestrated cluster, and mons' FQDN is
          # resolved through k8s service, set dns policy to cluster first
          dnsPolicy: ClusterFirstWithHostNet
          containers:
            - name: driver-registrar
              image: {{ .RegistrarImage }}
              args:
                - "--v=5"
                - "--csi-address=/csi/csi.sock"
                - "--kubelet-registration-path=/var/lib/kubelet/plugins/{{ .DriverNamePrefix }}cephfs.csi.ceph.com/csi.sock"
              lifecycle:
                preStop:
                  exec:
                      command: ["/bin/sh", "-c", "rm -rf /registration/csi-cephfsplugin /registration/csi-cephfsplugin-reg.sock"]
              env:
                - name: KUBE_NODE_NAME
                  valueFrom:
                    fieldRef:
                      fieldPath: spec.nodeName
              volumeMounts:
                - name: plugin-dir
                  mountPath: /csi
                - name: registration-dir
                  mountPath: /registration
            - name: csi-cephfsplugin
              securityContext:
                privileged: true
                capabilities:
                  add: ["SYS_ADMIN"]
                allowPrivilegeEscalation: true
              image: {{ .CSIPluginImage }}
              args :
                - "--nodeid=$(NODE_ID)"
                - "--endpoint=$(CSI_ENDPOINT)"
                - "--v=5"
                - "--type=cephfs"
                - "--drivername={{ .DriverNamePrefix }}cephfs.csi.ceph.com"
              env:
                - name: NODE_ID
                  valueFrom:
                    fieldRef:
                      fieldPath: spec.nodeName
                - name: POD_NAMESPACE
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.namespace
                - name: CSI_ENDPOINT
                  value: unix:///csi/csi.sock
              imagePullPolicy: "IfNotPresent"
              volumeMounts:
                - name: plugin-dir
                  mountPath: /csi
                - name: csi-plugins-dir
                  mountPath: /var/lib/kubelet/plugins
                  mountPropagation: "Bidirectional"
                - name: pods-mount-dir
                  mountPath: /var/lib/kubelet/pods
                  mountPropagation: "Bidirectional"
                - name: host-sys
                  mountPath: /sys
                - name: lib-modules
                  mountPath: /lib/modules
                  readOnly: true
                - name: host-dev
                  mountPath: /dev
                - name: ceph-csi-config
                  mountPath: /etc/ceph-csi-config/
                - name: keys-tmp-dir
                  mountPath: /tmp/csi/keys
                - name: host-run-mount
                  mountPath: /run/mount
          volumes:
            - name: plugin-dir
              hostPath:
                path: /var/lib/kubelet/plugins/{{ .DriverNamePrefix }}cephfs.csi.ceph.com/
                type: DirectoryOrCreate
            - name: csi-plugins-dir
              hostPath:
                path: /var/lib/kubelet/plugins
                type: Directory
            - name: registration-dir
              hostPath:
                path: /var/lib/kubelet/plugins_registry/
                type: Directory
            - name: pods-mount-dir
              hostPath:
                path: /var/lib/kubelet/pods
                type: Directory
            - name: host-sys
              hostPath:
                path: /sys
            - name: lib-modules
              hostPath:
                path: /lib/modules
            - name: host-dev
              hostPath:
                path: /dev
            - name: ceph-csi-config
              configMap:
                name: rook-ceph-csi-config
                items:
                  - key: csi-cluster-config-json
                    path: config.json
            - name: keys-tmp-dir
              emptyDir: {
                medium: "Memory"
              }
            - name: host-run-mount
              hostPath:
                path: /run/mount
`
)
<commit_msg>ceph: update e2e manifest for cephfs snapshotter sidecar<commit_after>/*
Copyright 2019 The Rook Authors. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
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.
*/

package installer

const (
	rbdProvisionerTemplate = `
    kind: StatefulSet
    apiVersion: apps/v1
    metadata:
      name: csi-rbdplugin-provisioner
      namespace: {{ .Namespace }}
    spec:
      serviceName: "csi-rbdplugin-provisioner"
      replicas: 1
      selector:
        matchLabels:
         app: csi-rbdplugin-provisioner
      template:
        metadata:
          labels:
            app: csi-rbdplugin-provisioner
        spec:
          serviceAccount: rook-csi-rbd-provisioner-sa
          containers:
            - name: csi-provisioner
              image: {{ .ProvisionerImage }}
              args:
                - "--csi-address=$(ADDRESS)"
                - "--v=5"
                - "--timeout=60s"
                - "--retry-interval-start=500ms"
              env:
                - name: ADDRESS
                  value: unix:///csi/csi-provisioner.sock
              imagePullPolicy: "IfNotPresent"
              volumeMounts:
                - name: socket-dir
                  mountPath: /csi
            - name: csi-rbdplugin-attacher
              image: {{ .AttacherImage }}
              args:
                - "--v=5"
                - "--csi-address=$(ADDRESS)"
              env:
                - name: ADDRESS
                  value: /csi/csi-provisioner.sock
              imagePullPolicy: "IfNotPresent"
              volumeMounts:
                - name: socket-dir
                  mountPath: /csi

            - name: csi-snapshotter
              image:  {{ .SnapshotterImage }}
              args:
                - "--csi-address=$(ADDRESS)"
                - "--v=5"
                - "--timeout=60s"
              env:
                - name: ADDRESS
                  value: unix:///csi/csi-provisioner.sock
              imagePullPolicy: Always
              securityContext:
                privileged: true
              volumeMounts:
                - name: socket-dir
                  mountPath: /csi
            - name: csi-rbdplugin
              securityContext:
                privileged: true
                capabilities:
                  add: ["SYS_ADMIN"]
              image: {{ .CSIPluginImage }}
              args :
                - "--nodeid=$(NODE_ID)"
                - "--endpoint=$(CSI_ENDPOINT)"
                - "--v=5"
                - "--type=rbd"
                - "--drivername={{ .DriverNamePrefix }}rbd.csi.ceph.com"
                - "--containerized=true"
                - "--pidlimit=-1"
              env:
                - name: HOST_ROOTFS
                  value: "/rootfs"
                - name: NODE_ID
                  valueFrom:
                    fieldRef:
                      fieldPath: spec.nodeName
                - name: POD_NAMESPACE
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.namespace
                - name: CSI_ENDPOINT
                  value: unix:///csi/csi-provisioner.sock
              imagePullPolicy: "IfNotPresent"
              volumeMounts:
                - name: socket-dir
                  mountPath: /csi
                - mountPath: /dev
                  name: host-dev
                - mountPath: /rootfs
                  name: host-rootfs
                - mountPath: /sys
                  name: host-sys
                - mountPath: /lib/modules
                  name: lib-modules
                  readOnly: true
                - name: ceph-csi-config
                  mountPath: /etc/ceph-csi-config/
                - name: keys-tmp-dir
                  mountPath: /tmp/csi/keys
          volumes:
            - name: host-dev
              hostPath:
                path: /dev
            - name: host-rootfs
              hostPath:
                path: /
            - name: host-sys
              hostPath:
                path: /sys
            - name: lib-modules
              hostPath:
                path: /lib/modules
            - name: socket-dir
              hostPath:
                path: /var/lib/kubelet/plugins/{{ .DriverNamePrefix }}rbd.csi.ceph.com
                type: DirectoryOrCreate
            - name: ceph-csi-config
              configMap:
                name: rook-ceph-csi-config
                items:
                  - key: csi-cluster-config-json
                    path: config.json
            - name: keys-tmp-dir
              emptyDir: {
                medium: "Memory"
              }
`
	rbdPluginTemplate = `
    kind: DaemonSet
    apiVersion: apps/v1
    metadata:
      name: csi-rbdplugin
      namespace: {{ .Namespace }}
    spec:
      selector:
        matchLabels:
          app: csi-rbdplugin
      template:
        metadata:
          labels:
            app: csi-rbdplugin
        spec:
          serviceAccount: rook-csi-rbd-plugin-sa
          hostNetwork: true
          hostPID: true
          # to use e.g. Rook orchestrated cluster, and mons' FQDN is
          # resolved through k8s service, set dns policy to cluster first
          dnsPolicy: ClusterFirstWithHostNet
          containers:
            - name: driver-registrar
              image: {{ .RegistrarImage }}
              args:
                - "--v=5"
                - "--csi-address=/csi/csi.sock"
                - "--kubelet-registration-path=/var/lib/kubelet/plugins/{{ .DriverNamePrefix }}rbd.csi.ceph.com/csi.sock"
              lifecycle:
                preStop:
                  exec:
                      command: ["/bin/sh", "-c", "rm -rf /registration/csi-rbdplugin /registration/csi-rbdplugin-reg.sock"]
              env:
                - name: KUBE_NODE_NAME
                  valueFrom:
                    fieldRef:
                      fieldPath: spec.nodeName
              volumeMounts:
                - name: plugin-dir
                  mountPath: /csi
                - name: registration-dir
                  mountPath: /registration
            - name: csi-rbdplugin
              securityContext:
                privileged: true
                capabilities:
                  add: ["SYS_ADMIN"]
                allowPrivilegeEscalation: true
              image: {{ .CSIPluginImage }}
              args :
                - "--nodeid=$(NODE_ID)"
                - "--endpoint=$(CSI_ENDPOINT)"
                - "--v=5"
                - "--type=rbd"
                - "--drivername={{ .DriverNamePrefix }}rbd.csi.ceph.com"
                - "--containerized=true"
              env:
                - name: HOST_ROOTFS
                  value: "/rootfs"
                - name: NODE_ID
                  valueFrom:
                    fieldRef:
                      fieldPath: spec.nodeName
                - name: POD_NAMESPACE
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.namespace
                - name: CSI_ENDPOINT
                  value: unix:///csi/csi.sock
              imagePullPolicy: "IfNotPresent"
              volumeMounts:
                - name: plugin-dir
                  mountPath: /csi
                - name: pods-mount-dir
                  mountPath: /var/lib/kubelet/pods
                  mountPropagation: "Bidirectional"
                - name: plugin-mount-dir
                  mountPath: /var/lib/kubelet/plugins
                  mountPropagation: "Bidirectional"
                - mountPath: /dev
                  name: host-dev
                - mountPath: /rootfs
                  name: host-rootfs
                - mountPath: /sys
                  name: host-sys
                - mountPath: /lib/modules
                  name: lib-modules
                  readOnly: true
                - name: ceph-csi-config
                  mountPath: /etc/ceph-csi-config/
                - name: keys-tmp-dir
                  mountPath: /tmp/csi/keys
                - name: host-run-mount
                  mountPath: /run/mount
          volumes:
            - name: plugin-dir
              hostPath:
                path: /var/lib/kubelet/plugins/{{ .DriverNamePrefix }}rbd.csi.ceph.com
                type: DirectoryOrCreate
            - name: plugin-mount-dir
              hostPath:
                path: /var/lib/kubelet/plugins
                type: Directory
            - name: registration-dir
              hostPath:
                path: /var/lib/kubelet/plugins_registry/
                type: Directory
            - name: pods-mount-dir
              hostPath:
                path: /var/lib/kubelet/pods
                type: Directory
            - name: host-dev
              hostPath:
                path: /dev
            - name: host-rootfs
              hostPath:
                path: /
            - name: host-sys
              hostPath:
                path: /sys
            - name: lib-modules
              hostPath:
                path: /lib/modules
            - name: ceph-csi-config
              configMap:
                name: rook-ceph-csi-config
                items:
                  - key: csi-cluster-config-json
                    path: config.json
            - name: keys-tmp-dir
              emptyDir: {
                medium: "Memory"
              }
            - name: host-run-mount
              hostPath:
                path: /run/mount
    `
	cephfsProvisionerTemplate = `
    kind: StatefulSet
    apiVersion: apps/v1
    metadata:
      name: csi-cephfsplugin-provisioner
      namespace: {{ .Namespace }}
    spec:
      serviceName: "csi-cephfsplugin-provisioner"
      replicas: 1
      selector:
        matchLabels:
         app: csi-cephfsplugin-provisioner
      template:
        metadata:
          labels:
            app: csi-cephfsplugin-provisioner
        spec:
          serviceAccount: rook-csi-cephfs-provisioner-sa
          containers:
            - name: csi-provisioner
              image: {{ .ProvisionerImage }}
              args:
                - "--csi-address=$(ADDRESS)"
                - "--v=5"
                - "--timeout=60s"
                - "--retry-interval-start=500ms"
              env:
                - name: ADDRESS
                  value: unix:///csi/csi-provisioner.sock
              imagePullPolicy: "IfNotPresent"
              volumeMounts:
                - name: socket-dir
                  mountPath: /csi
            - name: csi-attacher
              image: {{ .AttacherImage }}
              args:
                - "--v=5"
                - "--csi-address=$(ADDRESS)"
              env:
                - name: ADDRESS
                  value: /csi/csi-provisioner.sock
              imagePullPolicy: "IfNotPresent"
              volumeMounts:
                - name: socket-dir
                  mountPath: /csi
            - name: csi-snapshotter
              image:  {{ .SnapshotterImage }}
              args:
                - "--csi-address=$(ADDRESS)"
                - "--v=5"
                - "--timeout=60s"
              env:
                - name: ADDRESS
                  value: unix:///csi/csi-provisioner.sock
              imagePullPolicy: Always
              securityContext:
                privileged: true
              volumeMounts:
                - name: socket-dir
                  mountPath: /csi
            - name: csi-cephfsplugin
              securityContext:
                privileged: true
                capabilities:
                  add: ["SYS_ADMIN"]
              image: {{ .CSIPluginImage }}
              args :
                - "--nodeid=$(NODE_ID)"
                - "--endpoint=$(CSI_ENDPOINT)"
                - "--v=5"
                - "--type=cephfs"
                - "--drivername={{ .DriverNamePrefix }}cephfs.csi.ceph.com"
                - "--pidlimit=-1"
              env:
                - name: NODE_ID
                  valueFrom:
                    fieldRef:
                      fieldPath: spec.nodeName
                - name: POD_NAMESPACE
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.namespace
                - name: CSI_ENDPOINT
                  value: unix:///csi/csi-provisioner.sock
              imagePullPolicy: "IfNotPresent"
              volumeMounts:
                - name: socket-dir
                  mountPath: /csi
                - name: host-sys
                  mountPath: /sys
                - name: lib-modules
                  mountPath: /lib/modules
                  readOnly: true
                - name: host-dev
                  mountPath: /dev
                - name: ceph-csi-config
                  mountPath: /etc/ceph-csi-config/
                - name: keys-tmp-dir
                  mountPath: /tmp/csi/keys
          volumes:
            - name: socket-dir
              hostPath:
                path: /var/lib/kubelet/plugins/{{ .DriverNamePrefix }}cephfs.csi.ceph.com
                type: DirectoryOrCreate
            - name: host-sys
              hostPath:
                path: /sys
            - name: lib-modules
              hostPath:
                path: /lib/modules
            - name: host-dev
              hostPath:
                path: /dev
            - name: ceph-csi-config
              configMap:
                name: rook-ceph-csi-config
                items:
                  - key: csi-cluster-config-json
                    path: config.json
            - name: keys-tmp-dir
              emptyDir: {
                medium: "Memory"
              }
`
	cephfsPluginTemplate = `
    kind: DaemonSet
    apiVersion: apps/v1
    metadata:
      name: csi-cephfsplugin
      namespace: {{ .Namespace }}
    spec:
      selector:
        matchLabels:
          app: csi-cephfsplugin
      template:
        metadata:
          labels:
            app: csi-cephfsplugin
        spec:
          serviceAccount: rook-csi-cephfs-plugin-sa
          hostNetwork: true
          # to use e.g. Rook orchestrated cluster, and mons' FQDN is
          # resolved through k8s service, set dns policy to cluster first
          dnsPolicy: ClusterFirstWithHostNet
          containers:
            - name: driver-registrar
              image: {{ .RegistrarImage }}
              args:
                - "--v=5"
                - "--csi-address=/csi/csi.sock"
                - "--kubelet-registration-path=/var/lib/kubelet/plugins/{{ .DriverNamePrefix }}cephfs.csi.ceph.com/csi.sock"
              lifecycle:
                preStop:
                  exec:
                      command: ["/bin/sh", "-c", "rm -rf /registration/csi-cephfsplugin /registration/csi-cephfsplugin-reg.sock"]
              env:
                - name: KUBE_NODE_NAME
                  valueFrom:
                    fieldRef:
                      fieldPath: spec.nodeName
              volumeMounts:
                - name: plugin-dir
                  mountPath: /csi
                - name: registration-dir
                  mountPath: /registration
            - name: csi-cephfsplugin
              securityContext:
                privileged: true
                capabilities:
                  add: ["SYS_ADMIN"]
                allowPrivilegeEscalation: true
              image: {{ .CSIPluginImage }}
              args :
                - "--nodeid=$(NODE_ID)"
                - "--endpoint=$(CSI_ENDPOINT)"
                - "--v=5"
                - "--type=cephfs"
                - "--drivername={{ .DriverNamePrefix }}cephfs.csi.ceph.com"
              env:
                - name: NODE_ID
                  valueFrom:
                    fieldRef:
                      fieldPath: spec.nodeName
                - name: POD_NAMESPACE
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.namespace
                - name: CSI_ENDPOINT
                  value: unix:///csi/csi.sock
              imagePullPolicy: "IfNotPresent"
              volumeMounts:
                - name: plugin-dir
                  mountPath: /csi
                - name: csi-plugins-dir
                  mountPath: /var/lib/kubelet/plugins
                  mountPropagation: "Bidirectional"
                - name: pods-mount-dir
                  mountPath: /var/lib/kubelet/pods
                  mountPropagation: "Bidirectional"
                - name: host-sys
                  mountPath: /sys
                - name: lib-modules
                  mountPath: /lib/modules
                  readOnly: true
                - name: host-dev
                  mountPath: /dev
                - name: ceph-csi-config
                  mountPath: /etc/ceph-csi-config/
                - name: keys-tmp-dir
                  mountPath: /tmp/csi/keys
                - name: host-run-mount
                  mountPath: /run/mount
          volumes:
            - name: plugin-dir
              hostPath:
                path: /var/lib/kubelet/plugins/{{ .DriverNamePrefix }}cephfs.csi.ceph.com/
                type: DirectoryOrCreate
            - name: csi-plugins-dir
              hostPath:
                path: /var/lib/kubelet/plugins
                type: Directory
            - name: registration-dir
              hostPath:
                path: /var/lib/kubelet/plugins_registry/
                type: Directory
            - name: pods-mount-dir
              hostPath:
                path: /var/lib/kubelet/pods
                type: Directory
            - name: host-sys
              hostPath:
                path: /sys
            - name: lib-modules
              hostPath:
                path: /lib/modules
            - name: host-dev
              hostPath:
                path: /dev
            - name: ceph-csi-config
              configMap:
                name: rook-ceph-csi-config
                items:
                  - key: csi-cluster-config-json
                    path: config.json
            - name: keys-tmp-dir
              emptyDir: {
                medium: "Memory"
              }
            - name: host-run-mount
              hostPath:
                path: /run/mount
`
)
<|endoftext|>


<commit_before>package mock

import (
	"sync"

	"github.com/micro/go-micro/registry"
)

type mockRegistry struct {
	sync.RWMutex
	Services map[string][]*registry.Service
}

var (
	mockData = map[string][]*registry.Service{
		"foo": []*registry.Service{
			{
				Name:    "foo",
				Version: "1.0.0",
				Nodes: []*registry.Node{
					{
						Id:      "foo-1.0.0-123",
						Address: "localhost",
						Port:    9999,
					},
					{
						Id:      "foo-1.0.0-321",
						Address: "localhost",
						Port:    9999,
					},
				},
			},
			{
				Name:    "foo",
				Version: "1.0.1",
				Nodes: []*registry.Node{
					{
						Id:      "foo-1.0.1-321",
						Address: "localhost",
						Port:    6666,
					},
				},
			},
			{
				Name:    "foo",
				Version: "1.0.3",
				Nodes: []*registry.Node{
					{
						Id:      "foo-1.0.3-345",
						Address: "localhost",
						Port:    8888,
					},
				},
			},
		},
	}
)

func (m *mockRegistry) init() {
	m.Lock()
	defer m.Unlock()

	// add some mock data
	m.Services = mockData
}

func (m *mockRegistry) GetService(service string) ([]*registry.Service, error) {
	m.Lock()
	defer m.Unlock()

	s, ok := m.Services[service]
	if !ok || len(s) == 0 {
		return nil, registry.ErrNotFound
	}
	return s, nil

}

func (m *mockRegistry) ListServices() ([]*registry.Service, error) {
	m.Lock()
	defer m.Unlock()

	var services []*registry.Service
	for _, service := range m.Services {
		services = append(services, service...)
	}
	return services, nil
}

func (m *mockRegistry) Register(s *registry.Service, opts ...registry.RegisterOption) error {
	m.Lock()
	defer m.Unlock()

	services := addServices(m.Services[s.Name], []*registry.Service{s})
	m.Services[s.Name] = services
	return nil
}

func (m *mockRegistry) Deregister(s *registry.Service) error {
	m.Lock()
	defer m.Unlock()

	services := delServices(m.Services[s.Name], []*registry.Service{s})
	m.Services[s.Name] = services
	return nil
}

func (m *mockRegistry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) {
	var wopts registry.WatchOptions
	for _, o := range opts {
		o(&wopts)
	}
	return &mockWatcher{exit: make(chan bool), opts: wopts}, nil
}

func (m *mockRegistry) String() string {
	return "mock"
}

func (m *mockRegistry) Init(opts ...registry.Option) error {
	return nil
}

func (m *mockRegistry) Options() registry.Options {
	return registry.Options{}
}

func NewRegistry(opts ...registry.Options) registry.Registry {
	m := &mockRegistry{Services: make(map[string][]*registry.Service)}
	m.init()
	return m
}
<commit_msg>add package comment<commit_after>// Package mock provides a mock registry for testing
package mock

import (
	"sync"

	"github.com/micro/go-micro/registry"
)

type mockRegistry struct {
	sync.RWMutex
	Services map[string][]*registry.Service
}

var (
	mockData = map[string][]*registry.Service{
		"foo": []*registry.Service{
			{
				Name:    "foo",
				Version: "1.0.0",
				Nodes: []*registry.Node{
					{
						Id:      "foo-1.0.0-123",
						Address: "localhost",
						Port:    9999,
					},
					{
						Id:      "foo-1.0.0-321",
						Address: "localhost",
						Port:    9999,
					},
				},
			},
			{
				Name:    "foo",
				Version: "1.0.1",
				Nodes: []*registry.Node{
					{
						Id:      "foo-1.0.1-321",
						Address: "localhost",
						Port:    6666,
					},
				},
			},
			{
				Name:    "foo",
				Version: "1.0.3",
				Nodes: []*registry.Node{
					{
						Id:      "foo-1.0.3-345",
						Address: "localhost",
						Port:    8888,
					},
				},
			},
		},
	}
)

func (m *mockRegistry) init() {
	m.Lock()
	defer m.Unlock()

	// add some mock data
	m.Services = mockData
}

func (m *mockRegistry) GetService(service string) ([]*registry.Service, error) {
	m.Lock()
	defer m.Unlock()

	s, ok := m.Services[service]
	if !ok || len(s) == 0 {
		return nil, registry.ErrNotFound
	}
	return s, nil

}

func (m *mockRegistry) ListServices() ([]*registry.Service, error) {
	m.Lock()
	defer m.Unlock()

	var services []*registry.Service
	for _, service := range m.Services {
		services = append(services, service...)
	}
	return services, nil
}

func (m *mockRegistry) Register(s *registry.Service, opts ...registry.RegisterOption) error {
	m.Lock()
	defer m.Unlock()

	services := addServices(m.Services[s.Name], []*registry.Service{s})
	m.Services[s.Name] = services
	return nil
}

func (m *mockRegistry) Deregister(s *registry.Service) error {
	m.Lock()
	defer m.Unlock()

	services := delServices(m.Services[s.Name], []*registry.Service{s})
	m.Services[s.Name] = services
	return nil
}

func (m *mockRegistry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) {
	var wopts registry.WatchOptions
	for _, o := range opts {
		o(&wopts)
	}
	return &mockWatcher{exit: make(chan bool), opts: wopts}, nil
}

func (m *mockRegistry) String() string {
	return "mock"
}

func (m *mockRegistry) Init(opts ...registry.Option) error {
	return nil
}

func (m *mockRegistry) Options() registry.Options {
	return registry.Options{}
}

func NewRegistry(opts ...registry.Options) registry.Registry {
	m := &mockRegistry{Services: make(map[string][]*registry.Service)}
	m.init()
	return m
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/x509"
	"encoding/pem"
	"flag"
        "fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"os/exec"
	"path"
	"regexp"
)

var db = map[string]string{}
var csrLocation = "/var/csr/"
var crtLocation = "/var/crt/"
var confLocation = "/opt/pollendina/openssl-ca.cnf"

var port = flag.Int("port", 33004, "Default port for Pollendina CA.")

type Tuple struct{ CN, Token string }

var updates = make(chan Tuple)

var (
	Info    *log.Logger
	Warning *log.Logger
	Error   *log.Logger
)

func InitLogs(
	infoHandle io.Writer,
	warningHandle io.Writer,
	errorHandle io.Writer) {

	Info = log.New(infoHandle,
		"INFO: ",
		log.Ldate|log.Ltime|log.Lshortfile)

	Warning = log.New(warningHandle,
		"WARNING: ",
		log.Ldate|log.Ltime|log.Lshortfile)

	Error = log.New(errorHandle,
		"ERROR: ",
		log.Ldate|log.Ltime|log.Lshortfile)
}

func main() {

	flag.Parse()

	InitLogs(os.Stdout, os.Stdout, os.Stderr)

	rh := new(RegexHandler)

	authPathPattern, _ := regexp.Compile("/v1/authorize")
	signPathPattern, _ := regexp.Compile("/v1/sign/.*")

	rh.HandleFunc(authPathPattern, Authorize)
	rh.HandleFunc(signPathPattern, Sign)

	go MapWriter()

	// Placeholder for authentication / authorization middleware on authorize call.

	err := http.ListenAndServe(*port, rh)
	if err != nil {
		Error.Println(err)
	}
}

func MapWriter() {
	for {
		select {
		case t, ok := <-updates:
			if !ok {
				Error.Println("Publisher channel closed. Stopping.")
				return
			}
			Info.Println("Setting key %s to value %s", t.Token, t.CN)
			db[t.Token] = t.CN
		}
	}
}

func Authorize(w http.ResponseWriter, req *http.Request) {
	Info.Println("Received authorize call.")
	// Parse input
	cn := req.FormValue("cn")
	token := req.FormValue("token")

	// queue for write to map
	// ...
	t := Tuple{cn, token}
	updates <- t

	Info.Println("Service: %s", cn)
	Info.Println("Token: %s", token)

	req.Body.Close()
}

func Sign(w http.ResponseWriter, req *http.Request) {
	Info.Println("Received sign call.")

	// Pull the token out of the path
	_, token := path.Split(req.URL.Path)
	Info.Println("Received signing request for token %s", token)

	if len(token) == 0 {
		Warning.Println("No token provided.")
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	// Get the registered CN for the provided token (or fail)
	authCn := db[token]

	if authCn == "" {
		Warning.Println("Unauthorized CN.")
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	// Upload the CSR and copy it to some known location
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		Error.Println(err.Error())
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	randoName := fmt.Sprintf("%d.csr", rand.Int63())
	csrFilename := csrLocation + randoName
	err = ioutil.WriteFile(csrFilename, body, 0777)
	if err != nil {
		Error.Println(err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	Info.Println("File uploaded.")

	// Parse the CSR
	rawCSR, _ := ioutil.ReadFile(csrFilename)
	decodedCSR, _ := pem.Decode(rawCSR)
	csr, err := x509.ParseCertificateRequest(decodedCSR.Bytes)
	if err != nil {
		Error.Println(err.Error())
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	Info.Println("Received CSR for: %s", csr.Subject.CommonName)

	// check authorization for the provided commonname
	if csr.Subject.CommonName != authCn {
		Warning.Println("Unauthorized CN %s", csr.Subject.CommonName)
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	// Build the command for exec
	// openssl ca -config openssl-ca.cnf -policy signing_policy -extensions signing_req -out servercert.pem -infiles servercert.csr
	app := "openssl"
	command := "ca"
	c_flag := "-config"
	p_flag := "-policy"
	p_value := "signing_policy"
	e_flag := "-extensions"
	e_value := "signing_req"
	o_flag := "-out"
	outputFile := crtLocation + randoName + ".crt"
	i_flag := "-infiles"
	b_flag := "-batch"

	// Sign the CSR with OpenSSL
	cmd := exec.Command(app, command, b_flag, c_flag, confLocation, p_flag, p_value, e_flag, e_value, o_flag, outputFile, i_flag, csrFilename)
	args := fmt.Sprintf("%s %s %s %s %s %s %s %s %s %s %s %s %s", app, command, b_flag, c_flag, confLocation, p_flag, p_value, e_flag, e_value, o_flag, outputFile, i_flag, csrFilename)
	fmt.Println(args)
	stdOut, err := cmd.Output()
	if err != nil {
		Error.Println("OpenSSL stdout: %s", string(stdOut))
		Error.Println("OpenSSL stderr: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	// Open the output file for reading and stream it back on the response
	outputData, err := ioutil.ReadFile(outputFile)
	w.Write(outputData)
}

type MuxRoute struct {
	pattern *regexp.Regexp
	handler http.Handler
}

type RegexHandler struct {
	rs []*MuxRoute
}

func (rh *RegexHandler) Handler(p *regexp.Regexp, h http.Handler) {
	rh.rs = append(rh.rs, &MuxRoute{p, h})
}

func (rh *RegexHandler) HandleFunc(p *regexp.Regexp, h func(http.ResponseWriter, *http.Request)) {
	rh.rs = append(rh.rs, &MuxRoute{p, http.HandlerFunc(h)})
}

func (rh *RegexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	for _, route := range rh.rs {
		if route.pattern.MatchString(r.URL.Path) {
			route.handler.ServeHTTP(w, r)
			return
		}
	}
	Warning.Println("Route not found: %s", r.URL.Path)
	// no pattern matched; send 404 response
	http.NotFound(w, r)
}
<commit_msg>Fix port.<commit_after>package main

import (
	"crypto/x509"
	"encoding/pem"
	"flag"
        "fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"os/exec"
	"path"
	"regexp"
)

var db = map[string]string{}
var csrLocation = "/var/csr/"
var crtLocation = "/var/crt/"
var confLocation = "/opt/pollendina/openssl-ca.cnf"

var port = flag.String("port", ":33004", "Default port for Pollendina CA.")

type Tuple struct{ CN, Token string }

var updates = make(chan Tuple)

var (
	Info    *log.Logger
	Warning *log.Logger
	Error   *log.Logger
)

func InitLogs(
	infoHandle io.Writer,
	warningHandle io.Writer,
	errorHandle io.Writer) {

	Info = log.New(infoHandle,
		"INFO: ",
		log.Ldate|log.Ltime|log.Lshortfile)

	Warning = log.New(warningHandle,
		"WARNING: ",
		log.Ldate|log.Ltime|log.Lshortfile)

	Error = log.New(errorHandle,
		"ERROR: ",
		log.Ldate|log.Ltime|log.Lshortfile)
}

func main() {

	flag.Parse()

	InitLogs(os.Stdout, os.Stdout, os.Stderr)

	rh := new(RegexHandler)

	authPathPattern, _ := regexp.Compile("/v1/authorize")
	signPathPattern, _ := regexp.Compile("/v1/sign/.*")

	rh.HandleFunc(authPathPattern, Authorize)
	rh.HandleFunc(signPathPattern, Sign)

	go MapWriter()

	// Placeholder for authentication / authorization middleware on authorize call.

	err := http.ListenAndServe(*port, rh)
	if err != nil {
		Error.Println(err)
	}
}

func MapWriter() {
	for {
		select {
		case t, ok := <-updates:
			if !ok {
				Error.Println("Publisher channel closed. Stopping.")
				return
			}
			Info.Println("Setting key %s to value %s", t.Token, t.CN)
			db[t.Token] = t.CN
		}
	}
}

func Authorize(w http.ResponseWriter, req *http.Request) {
	Info.Println("Received authorize call.")
	// Parse input
	cn := req.FormValue("cn")
	token := req.FormValue("token")

	// queue for write to map
	// ...
	t := Tuple{cn, token}
	updates <- t

	Info.Println("Service: %s", cn)
	Info.Println("Token: %s", token)

	req.Body.Close()
}

func Sign(w http.ResponseWriter, req *http.Request) {
	Info.Println("Received sign call.")

	// Pull the token out of the path
	_, token := path.Split(req.URL.Path)
	Info.Println("Received signing request for token %s", token)

	if len(token) == 0 {
		Warning.Println("No token provided.")
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	// Get the registered CN for the provided token (or fail)
	authCn := db[token]

	if authCn == "" {
		Warning.Println("Unauthorized CN.")
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	// Upload the CSR and copy it to some known location
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		Error.Println(err.Error())
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	randoName := fmt.Sprintf("%d.csr", rand.Int63())
	csrFilename := csrLocation + randoName
	err = ioutil.WriteFile(csrFilename, body, 0777)
	if err != nil {
		Error.Println(err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	Info.Println("File uploaded.")

	// Parse the CSR
	rawCSR, _ := ioutil.ReadFile(csrFilename)
	decodedCSR, _ := pem.Decode(rawCSR)
	csr, err := x509.ParseCertificateRequest(decodedCSR.Bytes)
	if err != nil {
		Error.Println(err.Error())
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	Info.Println("Received CSR for: %s", csr.Subject.CommonName)

	// check authorization for the provided commonname
	if csr.Subject.CommonName != authCn {
		Warning.Println("Unauthorized CN %s", csr.Subject.CommonName)
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	// Build the command for exec
	// openssl ca -config openssl-ca.cnf -policy signing_policy -extensions signing_req -out servercert.pem -infiles servercert.csr
	app := "openssl"
	command := "ca"
	c_flag := "-config"
	p_flag := "-policy"
	p_value := "signing_policy"
	e_flag := "-extensions"
	e_value := "signing_req"
	o_flag := "-out"
	outputFile := crtLocation + randoName + ".crt"
	i_flag := "-infiles"
	b_flag := "-batch"

	// Sign the CSR with OpenSSL
	cmd := exec.Command(app, command, b_flag, c_flag, confLocation, p_flag, p_value, e_flag, e_value, o_flag, outputFile, i_flag, csrFilename)
	args := fmt.Sprintf("%s %s %s %s %s %s %s %s %s %s %s %s %s", app, command, b_flag, c_flag, confLocation, p_flag, p_value, e_flag, e_value, o_flag, outputFile, i_flag, csrFilename)
	fmt.Println(args)
	stdOut, err := cmd.Output()
	if err != nil {
		Error.Println("OpenSSL stdout: %s", string(stdOut))
		Error.Println("OpenSSL stderr: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	// Open the output file for reading and stream it back on the response
	outputData, err := ioutil.ReadFile(outputFile)
	w.Write(outputData)
}

type MuxRoute struct {
	pattern *regexp.Regexp
	handler http.Handler
}

type RegexHandler struct {
	rs []*MuxRoute
}

func (rh *RegexHandler) Handler(p *regexp.Regexp, h http.Handler) {
	rh.rs = append(rh.rs, &MuxRoute{p, h})
}

func (rh *RegexHandler) HandleFunc(p *regexp.Regexp, h func(http.ResponseWriter, *http.Request)) {
	rh.rs = append(rh.rs, &MuxRoute{p, http.HandlerFunc(h)})
}

func (rh *RegexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	for _, route := range rh.rs {
		if route.pattern.MatchString(r.URL.Path) {
			route.handler.ServeHTTP(w, r)
			return
		}
	}
	Warning.Println("Route not found: %s", r.URL.Path)
	// no pattern matched; send 404 response
	http.NotFound(w, r)
}
<|endoftext|>


<commit_before>package block

import (
	"fmt"
	"github.com/tajtiattila/joyster/block/parser"
	"time"
)

type Profile struct {
	D       time.Duration
	Blocks  []Block
	Tickers []Ticker
	Names   map[Block]string
}

func Parse(src string) (*Profile, error) {
	return ParseProfile(src, DefaultTypeMap)
}

func ParseProfile(src string, tm TypeMap) (*Profile, error) {
	p, err := parser.Parse(src, newParserTypeMap(tm))
	if err != nil {
		return nil, err
	}
	return instantiate(p, tm)
}

func Load(fn string) (*Profile, error) {
	return LoadProfile(fn, DefaultTypeMap)
}

func LoadProfile(fn string, tm TypeMap) (*Profile, error) {
	p, err := parser.LoadProfile(fn, newParserTypeMap(tm))
	if err != nil {
		return nil, err
	}
	return instantiate(p, tm)
}

func (p *Profile) Tick() {
	for _, t := range p.Tickers {
		t.Tick()
	}
}

func (p *Profile) Close() error {
	var firsterr error
	for _, blk := range p.Blocks {
		if c, ok := blk.(Closer); ok {
			err := c.Close()
			if firsterr == nil {
				firsterr = err
			}
		}
	}
	p.Blocks = nil
	p.Tickers = nil
	return firsterr
}

func instantiate(pprof *parser.Profile, tm TypeMap) (p *Profile, err error) {
	p = new(Profile)
	v, ok := pprof.Config[defaultTickFreqName]
	if !ok {
		v = DefaultTickFreq
	}
	p.D = time.Duration(float64(time.Second) / v)
	defer func() {
		if err != nil {
			p.Close()
		}
	}()
	p.Names = make(map[Block]string)
	mblk := make(map[*parser.Blk]Block)
	for _, pb := range pprof.Blocks {
		ptyp, ok := pb.Type.(*parserType)
		if !ok {
			return nil, fmt.Errorf("unexpected type for block '%s'", pb.Name)
		}
		param := &parseParam{parser.NewParamReader(pb.Param, pprof.Config)}
		blk, err := ptyp.typ.New(param)
		if err != nil {
			return nil, err
		}
		if param.Err() != nil {
			return nil, fmt.Errorf("block '%s' setup error: %v", param.Err())
		}
		mblk[pb] = blk
		for name, port := range pb.Inputs {
			var p Port
			switch i := port.(type) {
			case *parser.BlkPortSource:
				iblk, ok := mblk[i.Blk]
				if !ok {
					return nil, fmt.Errorf("input block '%s' missing", i.Blk.Name)
				}
				if p, err = iblk.Output().Get(i.Sel); err != nil {
					return nil, fmt.Errorf("input port '%s' of block '%s' missing", i.Sel, i.Blk.Name)
				}
			case *parser.ValueSource:
				p = valuePort(i)
			default:
				return nil, fmt.Errorf("unexpected input '%s' for block '%s'", name, pb.Name)
			}
			if err = blk.Input().Set(name, p); err != nil {
				return nil, fmt.Errorf("can't set input '%s' on block '%s': %v", name, pb.Name, err)
			}
		}
		if err = blk.Validate(); err != nil {
			return nil, fmt.Errorf("loaded block '%s' invalid: %v", pb.Name, err)
		}
		p.Blocks = append(p.Blocks, blk)
		p.Names[blk] = pb.Name
		if t, ok := blk.(Ticker); ok {
			p.Tickers = append(p.Tickers, t)
		}
	}
	// do a test tick to see if everything is in order
	if err := testtick(p); err != nil {
		return nil, err
	}
	return p, nil
}

func testtick(p *Profile) (err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("panic occurred in first tick: %v", r)
		}
	}()
	p.Tick()
	return
}

func valuePort(vs *parser.ValueSource) Port {
	switch v := vs.Value.(type) {
	case bool:
		return &v
	case float64:
		return &v
	case int:
		return &v
	}
	return nil
}
<commit_msg>fix panic while instantiating erroneous profile<commit_after>package block

import (
	"fmt"
	"github.com/tajtiattila/joyster/block/parser"
	"time"
)

type Profile struct {
	D       time.Duration
	Blocks  []Block
	Tickers []Ticker
	Names   map[Block]string
}

func Parse(src string) (*Profile, error) {
	return ParseProfile(src, DefaultTypeMap)
}

func ParseProfile(src string, tm TypeMap) (*Profile, error) {
	p, err := parser.Parse(src, newParserTypeMap(tm))
	if err != nil {
		return nil, err
	}
	return instantiate(p, tm)
}

func Load(fn string) (*Profile, error) {
	return LoadProfile(fn, DefaultTypeMap)
}

func LoadProfile(fn string, tm TypeMap) (*Profile, error) {
	p, err := parser.LoadProfile(fn, newParserTypeMap(tm))
	if err != nil {
		return nil, err
	}
	return instantiate(p, tm)
}

func (p *Profile) Tick() {
	for _, t := range p.Tickers {
		t.Tick()
	}
}

func (p *Profile) Close() error {
	var firsterr error
	for _, blk := range p.Blocks {
		if c, ok := blk.(Closer); ok {
			err := c.Close()
			if firsterr == nil {
				firsterr = err
			}
		}
	}
	p.Blocks = nil
	p.Tickers = nil
	return firsterr
}

func instantiate(pprof *parser.Profile, tm TypeMap) (p *Profile, err error) {
	p = new(Profile)
	psave := p
	v, ok := pprof.Config[defaultTickFreqName]
	if !ok {
		v = DefaultTickFreq
	}
	p.D = time.Duration(float64(time.Second) / v)
	defer func() {
		if err != nil {
			psave.Close()
		}
	}()
	p.Names = make(map[Block]string)
	mblk := make(map[*parser.Blk]Block)
	for _, pb := range pprof.Blocks {
		ptyp, ok := pb.Type.(*parserType)
		if !ok {
			return nil, fmt.Errorf("unexpected type for block '%s'", pb.Name)
		}
		param := &parseParam{parser.NewParamReader(pb.Param, pprof.Config)}
		blk, err := ptyp.typ.New(param)
		if err != nil {
			return nil, err
		}
		if param.Err() != nil {
			return nil, fmt.Errorf("block '%s' setup error: %v", param.Err())
		}
		mblk[pb] = blk
		for name, port := range pb.Inputs {
			var p Port
			switch i := port.(type) {
			case *parser.BlkPortSource:
				iblk, ok := mblk[i.Blk]
				if !ok {
					return nil, fmt.Errorf("input block '%s' missing", i.Blk.Name)
				}
				if p, err = iblk.Output().Get(i.Sel); err != nil {
					return nil, fmt.Errorf("input port '%s' of block '%s' missing", i.Sel, i.Blk.Name)
				}
			case *parser.ValueSource:
				p = valuePort(i)
			default:
				return nil, fmt.Errorf("unexpected input '%s' for block '%s'", name, pb.Name)
			}
			if err = blk.Input().Set(name, p); err != nil {
				return nil, fmt.Errorf("can't set input '%s' on block '%s': %v", name, pb.Name, err)
			}
		}
		if err = blk.Validate(); err != nil {
			return nil, fmt.Errorf("loaded block '%s' invalid: %v", pb.Name, err)
		}
		p.Blocks = append(p.Blocks, blk)
		p.Names[blk] = pb.Name
		if t, ok := blk.(Ticker); ok {
			p.Tickers = append(p.Tickers, t)
		}
	}
	// do a test tick to see if everything is in order
	if err := testtick(p); err != nil {
		return nil, err
	}
	return p, nil
}

func testtick(p *Profile) (err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("panic occurred in first tick: %v", r)
		}
	}()
	p.Tick()
	return
}

func valuePort(vs *parser.ValueSource) Port {
	switch v := vs.Value.(type) {
	case bool:
		return &v
	case float64:
		return &v
	case int:
		return &v
	}
	return nil
}
<|endoftext|>


<commit_before>package osin

import (
	"errors"
	"net/http"
	"time"
)

// AccessRequestType is the type for OAuth param `grant_type`
type AccessRequestType string

const (
	AUTHORIZATION_CODE AccessRequestType = "authorization_code"
	REFRESH_TOKEN                        = "refresh_token"
	PASSWORD                             = "password"
	CLIENT_CREDENTIALS                   = "client_credentials"
	ASSERTION                            = "assertion"
	IMPLICIT                             = "__implicit"
)

// AccessRequest is a request for access tokens
type AccessRequest struct {
	Type          AccessRequestType
	Code          string
	Client        Client
	AuthorizeData *AuthorizeData
	AccessData    *AccessData
	RedirectUri   string
	Scope         string
	Username      string
	Password      string
	AssertionType string
	Assertion     string

	// Set if request is authorized
	Authorized bool

	// Token expiration in seconds. Change if different from default
	Expiration int32

	// Set if a refresh token should be generated
	GenerateRefresh bool

	// Data to be passed to storage. Not used by the library.
	UserData interface{}

	// HttpRequest *http.Request for special use
	HttpRequest *http.Request
}

// AccessData represents an access grant (tokens, expiration, client, etc)
type AccessData struct {
	// Client information
	Client Client

	// Authorize data, for authorization code
	AuthorizeData *AuthorizeData

	// Previous access data, for refresh token
	AccessData *AccessData

	// Access token
	AccessToken string

	// Refresh Token. Can be blank
	RefreshToken string

	// Token expiration in seconds
	ExpiresIn int32

	// Requested scope
	Scope string

	// Redirect Uri from request
	RedirectUri string

	// Date created
	CreatedAt time.Time

	// Data to be passed to storage. Not used by the library.
	UserData interface{}
}

// IsExpired returns true if access expired
func (d *AccessData) IsExpired() bool {
	return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second).Before(time.Now())
}

// ExpireAt returns the expiration date
func (d *AccessData) ExpireAt() time.Time {
	return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second)
}

// AccessTokenGen generates access tokens
type AccessTokenGen interface {
	GenerateAccessToken(data *AccessData, generaterefresh bool) (accesstoken string, refreshtoken string, err error)
}

// HandleAccessRequest is the http.HandlerFunc for handling access token requests
func (s *Server) HandleAccessRequest(w *Response, r *http.Request) *AccessRequest {
	// Only allow GET or POST
	if r.Method == "GET" {
		if !s.Config.AllowGetAccessRequest {
			w.SetError(E_INVALID_REQUEST, "")
			w.InternalError = errors.New("Request must be POST")
			return nil
		}
	} else if r.Method != "POST" {
		w.SetError(E_INVALID_REQUEST, "")
		w.InternalError = errors.New("Request must be POST")
		return nil
	}

	err := r.ParseForm()
	if err != nil {
		w.SetError(E_INVALID_REQUEST, "")
		w.InternalError = err
		return nil
	}

	grantType := AccessRequestType(r.Form.Get("grant_type"))
	if s.Config.AllowedAccessTypes.Exists(grantType) {
		switch grantType {
		case AUTHORIZATION_CODE:
			return s.handleAuthorizationCodeRequest(w, r)
		case REFRESH_TOKEN:
			return s.handleRefreshTokenRequest(w, r)
		case PASSWORD:
			return s.handlePasswordRequest(w, r)
		case CLIENT_CREDENTIALS:
			return s.handleClientCredentialsRequest(w, r)
		case ASSERTION:
			return s.handleAssertionRequest(w, r)
		}
	}

	w.SetError(E_UNSUPPORTED_GRANT_TYPE, "")
	return nil
}

func (s *Server) handleAuthorizationCodeRequest(w *Response, r *http.Request) *AccessRequest {
	// get client authentication
	auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
	if auth == nil {
		return nil
	}

	// generate access token
	ret := &AccessRequest{
		Type:            AUTHORIZATION_CODE,
		Code:            r.Form.Get("code"),
		RedirectUri:     r.Form.Get("redirect_uri"),
		GenerateRefresh: true,
		Expiration:      s.Config.AccessExpiration,
		HttpRequest:     r,
	}

	// "code" is required
	if ret.Code == "" {
		w.SetError(E_INVALID_GRANT, "")
		return nil
	}

	// must have a valid client
	if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
		return nil
	}

	// must be a valid authorization code
	var err error
	ret.AuthorizeData, err = w.Storage.LoadAuthorize(ret.Code)
	if err != nil {
		w.SetError(E_INVALID_GRANT, "")
		w.InternalError = err
		return nil
	}
	if ret.AuthorizeData == nil {
		w.SetError(E_UNAUTHORIZED_CLIENT, "")
		return nil
	}
	if ret.AuthorizeData.Client == nil {
		w.SetError(E_UNAUTHORIZED_CLIENT, "")
		return nil
	}
	if ret.AuthorizeData.Client.GetRedirectUri() == "" {
		w.SetError(E_UNAUTHORIZED_CLIENT, "")
		return nil
	}
	if ret.AuthorizeData.IsExpired() {
		w.SetError(E_INVALID_GRANT, "")
		return nil
	}

	// code must be from the client
	if ret.AuthorizeData.Client.GetId() != ret.Client.GetId() {
		w.SetError(E_INVALID_GRANT, "")
		return nil
	}

	// check redirect uri
	if ret.RedirectUri == "" {
		ret.RedirectUri = FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator)
	}
	if err = ValidateUriList(ret.Client.GetRedirectUri(), ret.RedirectUri, s.Config.RedirectUriSeparator); err != nil {
		w.SetError(E_INVALID_REQUEST, "")
		w.InternalError = err
		return nil
	}
	if ret.AuthorizeData.RedirectUri != ret.RedirectUri {
		w.SetError(E_INVALID_REQUEST, "")
		w.InternalError = errors.New("Redirect uri is different")
		return nil
	}

	// set rest of data
	ret.Scope = ret.AuthorizeData.Scope
	ret.UserData = ret.AuthorizeData.UserData

	return ret
}

func (s *Server) handleRefreshTokenRequest(w *Response, r *http.Request) *AccessRequest {
	// get client authentication
	auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
	if auth == nil {
		return nil
	}

	// generate access token
	ret := &AccessRequest{
		Type:            REFRESH_TOKEN,
		Code:            r.Form.Get("refresh_token"),
		Scope:           r.Form.Get("scope"),
		GenerateRefresh: true,
		Expiration:      s.Config.AccessExpiration,
		HttpRequest:     r,
	}

	// "refresh_token" is required
	if ret.Code == "" {
		w.SetError(E_INVALID_GRANT, "")
		return nil
	}

	// must have a valid client
	if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
		return nil
	}

	// must be a valid refresh code
	var err error
	ret.AccessData, err = w.Storage.LoadRefresh(ret.Code)
	if err != nil {
		w.SetError(E_INVALID_GRANT, "")
		w.InternalError = err
		return nil
	}
	if ret.AccessData == nil {
		w.SetError(E_UNAUTHORIZED_CLIENT, "")
		return nil
	}
	if ret.AccessData.Client == nil {
		w.SetError(E_UNAUTHORIZED_CLIENT, "")
		return nil
	}
	if ret.AccessData.Client.GetRedirectUri() == "" {
		w.SetError(E_UNAUTHORIZED_CLIENT, "")
		return nil
	}

	// client must be the same as the previous token
	if ret.AccessData.Client.GetId() != ret.Client.GetId() {
		w.SetError(E_INVALID_CLIENT, "")
		w.InternalError = errors.New("Client id must be the same from previous token")
		return nil

	}

	// set rest of data
	ret.RedirectUri = ret.AccessData.RedirectUri
	ret.UserData = ret.AccessData.UserData
	if ret.Scope == "" {
		ret.Scope = ret.AccessData.Scope
	}

	return ret
}

func (s *Server) handlePasswordRequest(w *Response, r *http.Request) *AccessRequest {
	// get client authentication
	auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
	if auth == nil {
		return nil
	}

	// generate access token
	ret := &AccessRequest{
		Type:            PASSWORD,
		Username:        r.Form.Get("username"),
		Password:        r.Form.Get("password"),
		Scope:           r.Form.Get("scope"),
		GenerateRefresh: true,
		Expiration:      s.Config.AccessExpiration,
		HttpRequest:     r,
	}

	// "username" and "password" is required
	if ret.Username == "" || ret.Password == "" {
		w.SetError(E_INVALID_GRANT, "")
		return nil
	}

	// must have a valid client
	if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
		return nil
	}

	// set redirect uri
	ret.RedirectUri = FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator)

	return ret
}

func (s *Server) handleClientCredentialsRequest(w *Response, r *http.Request) *AccessRequest {
	// get client authentication
	auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
	if auth == nil {
		return nil
	}

	// generate access token
	ret := &AccessRequest{
		Type:            CLIENT_CREDENTIALS,
		Scope:           r.Form.Get("scope"),
		GenerateRefresh: true,
		Expiration:      s.Config.AccessExpiration,
		HttpRequest:     r,
	}

	// must have a valid client
	if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
		return nil
	}

	// set redirect uri
	ret.RedirectUri = FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator)

	return ret
}

func (s *Server) handleAssertionRequest(w *Response, r *http.Request) *AccessRequest {
	// get client authentication
	auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
	if auth == nil {
		return nil
	}

	// generate access token
	ret := &AccessRequest{
		Type:            ASSERTION,
		Scope:           r.Form.Get("scope"),
		AssertionType:   r.Form.Get("assertion_type"),
		Assertion:       r.Form.Get("assertion"),
		GenerateRefresh: false, // assertion should NOT generate a refresh token, per the RFC
		Expiration:      s.Config.AccessExpiration,
		HttpRequest:     r,
	}

	// "assertion_type" and "assertion" is required
	if ret.AssertionType == "" || ret.Assertion == "" {
		w.SetError(E_INVALID_GRANT, "")
		return nil
	}

	// must have a valid client
	if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
		return nil
	}

	// set redirect uri
	ret.RedirectUri = FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator)

	return ret
}

func (s *Server) FinishAccessRequest(w *Response, r *http.Request, ar *AccessRequest) {
	// don't process if is already an error
	if w.IsError {
		return
	}
	redirectUri := r.Form.Get("redirect_uri")
	// Get redirect uri from AccessRequest if it's there (e.g., refresh token request)
	if ar.RedirectUri != "" {
		redirectUri = ar.RedirectUri
	}
	if ar.Authorized {
		// generate access token
		ret := &AccessData{
			Client:        ar.Client,
			AuthorizeData: ar.AuthorizeData,
			AccessData:    ar.AccessData,
			RedirectUri:   redirectUri,
			CreatedAt:     time.Now(),
			ExpiresIn:     ar.Expiration,
			UserData:      ar.UserData,
			Scope:         ar.Scope,
		}

		var err error

		// generate access token
		ret.AccessToken, ret.RefreshToken, err = s.AccessTokenGen.GenerateAccessToken(ret, ar.GenerateRefresh)
		if err != nil {
			w.SetError(E_SERVER_ERROR, "")
			w.InternalError = err
			return
		}

		// save access token
		if err = w.Storage.SaveAccess(ret); err != nil {
			w.SetError(E_SERVER_ERROR, "")
			w.InternalError = err
			return
		}

		// remove authorization token
		if ret.AuthorizeData != nil {
			w.Storage.RemoveAuthorize(ret.AuthorizeData.Code)
		}

		// remove previous access token
		if ret.AccessData != nil {
			if ret.AccessData.RefreshToken != "" {
				w.Storage.RemoveRefresh(ret.AccessData.RefreshToken)
			}
			w.Storage.RemoveAccess(ret.AccessData.AccessToken)
		}

		// output data
		w.Output["access_token"] = ret.AccessToken
		w.Output["token_type"] = s.Config.TokenType
		w.Output["expires_in"] = ret.ExpiresIn
		if ret.RefreshToken != "" {
			w.Output["refresh_token"] = ret.RefreshToken
		}
		if ar.Scope != "" {
			w.Output["scope"] = ar.Scope
		}
	} else {
		w.SetError(E_ACCESS_DENIED, "")
	}
}

// Helper Functions

// getClient looks up and authenticates the basic auth using the given
// storage. Sets an error on the response if auth fails or a server error occurs.
func getClient(auth *BasicAuth, storage Storage, w *Response) Client {
	client, err := storage.GetClient(auth.Username)
	if err != nil {
		w.SetError(E_SERVER_ERROR, "")
		w.InternalError = err
		return nil
	}
	if client == nil {
		w.SetError(E_UNAUTHORIZED_CLIENT, "")
		return nil
	}
	if client.GetSecret() != auth.Password {
		w.SetError(E_UNAUTHORIZED_CLIENT, "")
		return nil
	}
	if client.GetRedirectUri() == "" {
		w.SetError(E_UNAUTHORIZED_CLIENT, "")
		return nil
	}
	return client
}
<commit_msg>* Allows reuse of access token, for example the same user / client<commit_after>package osin

import (
	"errors"
	"net/http"
	"time"
)

// AccessRequestType is the type for OAuth param `grant_type`
type AccessRequestType string

const (
	AUTHORIZATION_CODE AccessRequestType = "authorization_code"
	REFRESH_TOKEN                        = "refresh_token"
	PASSWORD                             = "password"
	CLIENT_CREDENTIALS                   = "client_credentials"
	ASSERTION                            = "assertion"
	IMPLICIT                             = "__implicit"
)

// AccessRequest is a request for access tokens
type AccessRequest struct {
	Type          AccessRequestType
	Code          string
	Client        Client
	AuthorizeData *AuthorizeData
	AccessData    *AccessData

	// Force finish to use this access data, to allow access data reuse
	ForceAccessData *AccessData
	RedirectUri     string
	Scope           string
	Username        string
	Password        string
	AssertionType   string
	Assertion       string

	// Set if request is authorized
	Authorized bool

	// Token expiration in seconds. Change if different from default
	Expiration int32

	// Set if a refresh token should be generated
	GenerateRefresh bool

	// Data to be passed to storage. Not used by the library.
	UserData interface{}

	// HttpRequest *http.Request for special use
	HttpRequest *http.Request
}

// AccessData represents an access grant (tokens, expiration, client, etc)
type AccessData struct {
	// Client information
	Client Client

	// Authorize data, for authorization code
	AuthorizeData *AuthorizeData

	// Previous access data, for refresh token
	AccessData *AccessData

	// Access token
	AccessToken string

	// Refresh Token. Can be blank
	RefreshToken string

	// Token expiration in seconds
	ExpiresIn int32

	// Requested scope
	Scope string

	// Redirect Uri from request
	RedirectUri string

	// Date created
	CreatedAt time.Time

	// Data to be passed to storage. Not used by the library.
	UserData interface{}
}

// IsExpired returns true if access expired
func (d *AccessData) IsExpired() bool {
	return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second).Before(time.Now())
}

// ExpireAt returns the expiration date
func (d *AccessData) ExpireAt() time.Time {
	return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second)
}

// AccessTokenGen generates access tokens
type AccessTokenGen interface {
	GenerateAccessToken(data *AccessData, generaterefresh bool) (accesstoken string, refreshtoken string, err error)
}

// HandleAccessRequest is the http.HandlerFunc for handling access token requests
func (s *Server) HandleAccessRequest(w *Response, r *http.Request) *AccessRequest {
	// Only allow GET or POST
	if r.Method == "GET" {
		if !s.Config.AllowGetAccessRequest {
			w.SetError(E_INVALID_REQUEST, "")
			w.InternalError = errors.New("Request must be POST")
			return nil
		}
	} else if r.Method != "POST" {
		w.SetError(E_INVALID_REQUEST, "")
		w.InternalError = errors.New("Request must be POST")
		return nil
	}

	err := r.ParseForm()
	if err != nil {
		w.SetError(E_INVALID_REQUEST, "")
		w.InternalError = err
		return nil
	}

	grantType := AccessRequestType(r.Form.Get("grant_type"))
	if s.Config.AllowedAccessTypes.Exists(grantType) {
		switch grantType {
		case AUTHORIZATION_CODE:
			return s.handleAuthorizationCodeRequest(w, r)
		case REFRESH_TOKEN:
			return s.handleRefreshTokenRequest(w, r)
		case PASSWORD:
			return s.handlePasswordRequest(w, r)
		case CLIENT_CREDENTIALS:
			return s.handleClientCredentialsRequest(w, r)
		case ASSERTION:
			return s.handleAssertionRequest(w, r)
		}
	}

	w.SetError(E_UNSUPPORTED_GRANT_TYPE, "")
	return nil
}

func (s *Server) handleAuthorizationCodeRequest(w *Response, r *http.Request) *AccessRequest {
	// get client authentication
	auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
	if auth == nil {
		return nil
	}

	// generate access token
	ret := &AccessRequest{
		Type:            AUTHORIZATION_CODE,
		Code:            r.Form.Get("code"),
		RedirectUri:     r.Form.Get("redirect_uri"),
		GenerateRefresh: true,
		Expiration:      s.Config.AccessExpiration,
		HttpRequest:     r,
	}

	// "code" is required
	if ret.Code == "" {
		w.SetError(E_INVALID_GRANT, "")
		return nil
	}

	// must have a valid client
	if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
		return nil
	}

	// must be a valid authorization code
	var err error
	ret.AuthorizeData, err = w.Storage.LoadAuthorize(ret.Code)
	if err != nil {
		w.SetError(E_INVALID_GRANT, "")
		w.InternalError = err
		return nil
	}
	if ret.AuthorizeData == nil {
		w.SetError(E_UNAUTHORIZED_CLIENT, "")
		return nil
	}
	if ret.AuthorizeData.Client == nil {
		w.SetError(E_UNAUTHORIZED_CLIENT, "")
		return nil
	}
	if ret.AuthorizeData.Client.GetRedirectUri() == "" {
		w.SetError(E_UNAUTHORIZED_CLIENT, "")
		return nil
	}
	if ret.AuthorizeData.IsExpired() {
		w.SetError(E_INVALID_GRANT, "")
		return nil
	}

	// code must be from the client
	if ret.AuthorizeData.Client.GetId() != ret.Client.GetId() {
		w.SetError(E_INVALID_GRANT, "")
		return nil
	}

	// check redirect uri
	if ret.RedirectUri == "" {
		ret.RedirectUri = FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator)
	}
	if err = ValidateUriList(ret.Client.GetRedirectUri(), ret.RedirectUri, s.Config.RedirectUriSeparator); err != nil {
		w.SetError(E_INVALID_REQUEST, "")
		w.InternalError = err
		return nil
	}
	if ret.AuthorizeData.RedirectUri != ret.RedirectUri {
		w.SetError(E_INVALID_REQUEST, "")
		w.InternalError = errors.New("Redirect uri is different")
		return nil
	}

	// set rest of data
	ret.Scope = ret.AuthorizeData.Scope
	ret.UserData = ret.AuthorizeData.UserData

	return ret
}

func (s *Server) handleRefreshTokenRequest(w *Response, r *http.Request) *AccessRequest {
	// get client authentication
	auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
	if auth == nil {
		return nil
	}

	// generate access token
	ret := &AccessRequest{
		Type:            REFRESH_TOKEN,
		Code:            r.Form.Get("refresh_token"),
		Scope:           r.Form.Get("scope"),
		GenerateRefresh: true,
		Expiration:      s.Config.AccessExpiration,
		HttpRequest:     r,
	}

	// "refresh_token" is required
	if ret.Code == "" {
		w.SetError(E_INVALID_GRANT, "")
		return nil
	}

	// must have a valid client
	if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
		return nil
	}

	// must be a valid refresh code
	var err error
	ret.AccessData, err = w.Storage.LoadRefresh(ret.Code)
	if err != nil {
		w.SetError(E_INVALID_GRANT, "")
		w.InternalError = err
		return nil
	}
	if ret.AccessData == nil {
		w.SetError(E_UNAUTHORIZED_CLIENT, "")
		return nil
	}
	if ret.AccessData.Client == nil {
		w.SetError(E_UNAUTHORIZED_CLIENT, "")
		return nil
	}
	if ret.AccessData.Client.GetRedirectUri() == "" {
		w.SetError(E_UNAUTHORIZED_CLIENT, "")
		return nil
	}

	// client must be the same as the previous token
	if ret.AccessData.Client.GetId() != ret.Client.GetId() {
		w.SetError(E_INVALID_CLIENT, "")
		w.InternalError = errors.New("Client id must be the same from previous token")
		return nil

	}

	// set rest of data
	ret.RedirectUri = ret.AccessData.RedirectUri
	ret.UserData = ret.AccessData.UserData
	if ret.Scope == "" {
		ret.Scope = ret.AccessData.Scope
	}

	return ret
}

func (s *Server) handlePasswordRequest(w *Response, r *http.Request) *AccessRequest {
	// get client authentication
	auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
	if auth == nil {
		return nil
	}

	// generate access token
	ret := &AccessRequest{
		Type:            PASSWORD,
		Username:        r.Form.Get("username"),
		Password:        r.Form.Get("password"),
		Scope:           r.Form.Get("scope"),
		GenerateRefresh: true,
		Expiration:      s.Config.AccessExpiration,
		HttpRequest:     r,
	}

	// "username" and "password" is required
	if ret.Username == "" || ret.Password == "" {
		w.SetError(E_INVALID_GRANT, "")
		return nil
	}

	// must have a valid client
	if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
		return nil
	}

	// set redirect uri
	ret.RedirectUri = FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator)

	return ret
}

func (s *Server) handleClientCredentialsRequest(w *Response, r *http.Request) *AccessRequest {
	// get client authentication
	auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
	if auth == nil {
		return nil
	}

	// generate access token
	ret := &AccessRequest{
		Type:            CLIENT_CREDENTIALS,
		Scope:           r.Form.Get("scope"),
		GenerateRefresh: true,
		Expiration:      s.Config.AccessExpiration,
		HttpRequest:     r,
	}

	// must have a valid client
	if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
		return nil
	}

	// set redirect uri
	ret.RedirectUri = FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator)

	return ret
}

func (s *Server) handleAssertionRequest(w *Response, r *http.Request) *AccessRequest {
	// get client authentication
	auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
	if auth == nil {
		return nil
	}

	// generate access token
	ret := &AccessRequest{
		Type:            ASSERTION,
		Scope:           r.Form.Get("scope"),
		AssertionType:   r.Form.Get("assertion_type"),
		Assertion:       r.Form.Get("assertion"),
		GenerateRefresh: false, // assertion should NOT generate a refresh token, per the RFC
		Expiration:      s.Config.AccessExpiration,
		HttpRequest:     r,
	}

	// "assertion_type" and "assertion" is required
	if ret.AssertionType == "" || ret.Assertion == "" {
		w.SetError(E_INVALID_GRANT, "")
		return nil
	}

	// must have a valid client
	if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
		return nil
	}

	// set redirect uri
	ret.RedirectUri = FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator)

	return ret
}

func (s *Server) FinishAccessRequest(w *Response, r *http.Request, ar *AccessRequest) {
	// don't process if is already an error
	if w.IsError {
		return
	}
	redirectUri := r.Form.Get("redirect_uri")
	// Get redirect uri from AccessRequest if it's there (e.g., refresh token request)
	if ar.RedirectUri != "" {
		redirectUri = ar.RedirectUri
	}
	if ar.Authorized {
		var ret *AccessData
		var err error

		if ar.ForceAccessData == nil {
			// generate access token
			ret = &AccessData{
				Client:        ar.Client,
				AuthorizeData: ar.AuthorizeData,
				AccessData:    ar.AccessData,
				RedirectUri:   redirectUri,
				CreatedAt:     time.Now(),
				ExpiresIn:     ar.Expiration,
				UserData:      ar.UserData,
				Scope:         ar.Scope,
			}

			// generate access token
			ret.AccessToken, ret.RefreshToken, err = s.AccessTokenGen.GenerateAccessToken(ret, ar.GenerateRefresh)
			if err != nil {
				w.SetError(E_SERVER_ERROR, "")
				w.InternalError = err
				return
			}
		} else {
			ret = ar.ForceAccessData
		}

		// save access token
		if err = w.Storage.SaveAccess(ret); err != nil {
			w.SetError(E_SERVER_ERROR, "")
			w.InternalError = err
			return
		}

		// remove authorization token
		if ret.AuthorizeData != nil {
			w.Storage.RemoveAuthorize(ret.AuthorizeData.Code)
		}

		// remove previous access token
		if ret.AccessData != nil {
			if ret.AccessData.RefreshToken != "" {
				w.Storage.RemoveRefresh(ret.AccessData.RefreshToken)
			}
			w.Storage.RemoveAccess(ret.AccessData.AccessToken)
		}

		// output data
		w.Output["access_token"] = ret.AccessToken
		w.Output["token_type"] = s.Config.TokenType
		w.Output["expires_in"] = ret.ExpiresIn
		if ret.RefreshToken != "" {
			w.Output["refresh_token"] = ret.RefreshToken
		}
		if ar.Scope != "" {
			w.Output["scope"] = ar.Scope
		}
	} else {
		w.SetError(E_ACCESS_DENIED, "")
	}
}

// Helper Functions

// getClient looks up and authenticates the basic auth using the given
// storage. Sets an error on the response if auth fails or a server error occurs.
func getClient(auth *BasicAuth, storage Storage, w *Response) Client {
	client, err := storage.GetClient(auth.Username)
	if err != nil {
		w.SetError(E_SERVER_ERROR, "")
		w.InternalError = err
		return nil
	}
	if client == nil {
		w.SetError(E_UNAUTHORIZED_CLIENT, "")
		return nil
	}
	if client.GetSecret() != auth.Password {
		w.SetError(E_UNAUTHORIZED_CLIENT, "")
		return nil
	}
	if client.GetRedirectUri() == "" {
		w.SetError(E_UNAUTHORIZED_CLIENT, "")
		return nil
	}
	return client
}
<|endoftext|>


<commit_before>package slack

// ContextBlock defines data that is used to display message context, which can
// include both images and text.
//
// More Information: https://api.slack.com/reference/messaging/blocks#actions
type ContextBlock struct {
	Type     MessageBlockType `json:"type"`
	BlockID  string           `json:"block_id,omitempty"`
	Elements ContextElements  `json:"elements"`
}

// blockType returns the type of the block
func (s ContextBlock) blockType() MessageBlockType {
	return s.Type
}

type ContextElements struct {
	ImageElements []*ImageBlockElement
	TextObjects   []*TextBlockObject
}

// NewContextBlock returns a new instance of a context block
func NewContextBlock(blockID string, elements ContextElements) *ContextBlock {
	return &ContextBlock{
		Type:     mbtContext,
		BlockID:  blockID,
		Elements: elements,
	}
}
<commit_msg>Add convenience method for ContextElements<commit_after>package slack

// ContextBlock defines data that is used to display message context, which can
// include both images and text.
//
// More Information: https://api.slack.com/reference/messaging/blocks#actions
type ContextBlock struct {
	Type     MessageBlockType `json:"type"`
	BlockID  string           `json:"block_id,omitempty"`
	Elements ContextElements  `json:"elements"`
}

// blockType returns the type of the block
func (s ContextBlock) blockType() MessageBlockType {
	return s.Type
}

type ContextElements struct {
	ImageElements []*ImageBlockElement
	TextObjects   []*TextBlockObject
}

// NewContextElements is a convenience method for generating ContextElements
func NewContextElements(imageElements []*ImageBlockElement, textObjects []*TextBlockObject) ContextElements {
	return ContextElements{
		ImageElements: imageElements,
		TextObjects:   textObjects,
	}
}

// NewContextBlock returns a new instance of a context block
func NewContextBlock(blockID string, elements ContextElements) *ContextBlock {
	return &ContextBlock{
		Type:     mbtContext,
		BlockID:  blockID,
		Elements: elements,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"sort"
	"text/tabwriter"
	"time"

	"github.com/bgentry/heroku-go"
)

var cmdAccess = &Command{
	Run:      runAccess,
	Usage:    "access",
	NeedsApp: true,
	Category: "access",
	Short:    "list access permissions" + extra,
	Long: `
List access permissions for an app. The owner is shown first, and
collaborators are then listed alphabetically.

Examples:

    $ hk access
    b@heroku.com    owner
    max@heroku.com  collaborator
`,
}

func runAccess(cmd *Command, args []string) {
	w := tabwriter.NewWriter(os.Stdout, 1, 2, 2, ' ', 0)
	defer w.Flush()

	if len(args) != 0 {
		cmd.PrintUsage()
		os.Exit(2)
	}
	ma := getMergedAccess(mustApp())
	for _, m := range ma {
		listRec(w,
			m.User,
			m.Role,
			prettyTime{m.Time},
		)
	}
}

type mergedAccess struct {
	User string
	Role string
	Time time.Time
}

func getMergedAccess(appname string) []*mergedAccess {
	var collaborators []heroku.Collaborator
	var app *heroku.App
	ch := make(chan error)
	go func() {
		var err error
		collaborators, err = client.CollaboratorList(appname, nil)
		ch <- err
	}()
	go func() {
		var err error
		app, err = client.AppInfo(appname)
		ch <- err
	}()
	if err := <-ch; err != nil {
		printFatal(err.Error())
	}
	if err := <-ch; err != nil {
		printFatal(err.Error())
	}
	return mergeAccess(app, collaborators)
}

type accessByRoleAndUser []*mergedAccess

func (a accessByRoleAndUser) Len() int      { return len(a) }
func (a accessByRoleAndUser) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a accessByRoleAndUser) Less(i, j int) bool {
	return a[i].Role == "owner" || a[i].User < a[j].User
}

func mergeAccess(app *heroku.App, collaborators []heroku.Collaborator) (ma []*mergedAccess) {
	// User, Role, Time
	for _, c := range collaborators {
		role := "collaborator"
		if app.Owner.Email == c.User.Email {
			role = "owner"
		}
		m := &mergedAccess{
			User: c.User.Email,
			Role: role,
			Time: c.UpdatedAt,
		}
		ma = append(ma, m)
	}
	sort.Sort(accessByRoleAndUser(ma))
	return ma
}

var cmdAccessAdd = &Command{
	Run:      runAccessAdd,
	Usage:    "access-add [-s] <email>",
	NeedsApp: true,
	Category: "access",
	Short:    "give a user access to an app" + extra,
	Long: `
Give another Heroku user access to an app.

Options:

    -s  add user silently with no email notification

Examples:

    $ hk access-add user@me.com

    $ hk access-add -s anotheruser@me.com
`,
}

var flagSilent bool

func init() {
	cmdAccessAdd.Flag.BoolVarP(&flagSilent, "silent", "s", false, "add user silently with no email notification")
}

func runAccessAdd(cmd *Command, args []string) {
	appname := mustApp()
	if len(args) != 1 {
		cmd.PrintUsage()
		os.Exit(2)
	}
	opts := heroku.CollaboratorCreateOpts{Silent: &flagSilent}
	_, err := client.CollaboratorCreate(appname, args[0], &opts)
	must(err)
}

var cmdAccessRemove = &Command{
	Run:      runAccessRemove,
	Usage:    "access-remove <email>",
	NeedsApp: true,
	Category: "access",
	Short:    "remove a user's access to an app" + extra,
	Long: `
Remove another Heroku user's access to an app.

Examples:

    $ hk access-remove user@me.com
`,
}

func runAccessRemove(cmd *Command, args []string) {
	appname := mustApp()
	if len(args) != 1 {
		cmd.PrintUsage()
		os.Exit(2)
	}
	must(client.CollaboratorDelete(appname, args[0]))
}
<commit_msg>access listing for org apps<commit_after>package main

import (
	"os"
	"sort"
	"text/tabwriter"

	"github.com/bgentry/heroku-go"
)

var cmdAccess = &Command{
	Run:      runAccess,
	Usage:    "access",
	NeedsApp: true,
	Category: "access",
	Short:    "list access permissions" + extra,
	Long: `
List access permissions for an app. The owner is shown first, and
collaborators are then listed alphabetically.

Examples:

    $ hk access
    b@heroku.com    owner
    max@heroku.com  collaborator
`,
}

func runAccess(cmd *Command, args []string) {
	w := tabwriter.NewWriter(os.Stdout, 1, 2, 2, ' ', 0)
	defer w.Flush()

	if len(args) != 0 {
		cmd.PrintUsage()
		os.Exit(2)
	}

	// Org collaborators works for all apps and gives us exactly the data we need.
	orgCollaborators, err := client.OrganizationAppCollaboratorList(mustApp(), nil)
	must(err)

	sort.Sort(accessByRoleAndEmail(orgCollaborators))
	for _, oc := range orgCollaborators {
		listRec(w,
			oc.User.Email,
			oc.Role,
			prettyTime{oc.UpdatedAt},
		)
	}
}

type accessByRoleAndEmail []heroku.OrganizationAppCollaborator

func (a accessByRoleAndEmail) Len() int      { return len(a) }
func (a accessByRoleAndEmail) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a accessByRoleAndEmail) Less(i, j int) bool {
	return a[i].Role == "owner" || a[i].User.Email < a[j].User.Email
}

var cmdAccessAdd = &Command{
	Run:      runAccessAdd,
	Usage:    "access-add [-s] <email>",
	NeedsApp: true,
	Category: "access",
	Short:    "give a user access to an app" + extra,
	Long: `
Give another Heroku user access to an app.

Options:

    -s  add user silently with no email notification

Examples:

    $ hk access-add user@me.com

    $ hk access-add -s anotheruser@me.com
`,
}

var flagSilent bool

func init() {
	cmdAccessAdd.Flag.BoolVarP(&flagSilent, "silent", "s", false, "add user silently with no email notification")
}

func runAccessAdd(cmd *Command, args []string) {
	appname := mustApp()
	if len(args) != 1 {
		cmd.PrintUsage()
		os.Exit(2)
	}
	opts := heroku.CollaboratorCreateOpts{Silent: &flagSilent}
	_, err := client.CollaboratorCreate(appname, args[0], &opts)
	must(err)
}

var cmdAccessRemove = &Command{
	Run:      runAccessRemove,
	Usage:    "access-remove <email>",
	NeedsApp: true,
	Category: "access",
	Short:    "remove a user's access to an app" + extra,
	Long: `
Remove another Heroku user's access to an app.

Examples:

    $ hk access-remove user@me.com
`,
}

func runAccessRemove(cmd *Command, args []string) {
	appname := mustApp()
	if len(args) != 1 {
		cmd.PrintUsage()
		os.Exit(2)
	}
	must(client.CollaboratorDelete(appname, args[0]))
}
<|endoftext|>


<commit_before>package services

import (
	//	"github.com/justinsb/gova/collections"
	"github.com/justinsb/gova/errors"
	"github.com/justinsb/slf4g/log"

	"sync"
	//	"fmt"
	//	"io"
)

type ServiceMap struct {
	factory  fnServiceFactory
	services map[string]*ServiceSlot
	changes  chan ServiceConfig

	mutex sync.Mutex
}

type ServiceSlot struct {
	service interface{}
	channel chan<- ServiceConfig
}

type fnServiceFactory func(key string, channel <-chan ServiceConfig) interface{}

func NewServiceMap(factory fnServiceFactory) *ServiceMap {
	self := &ServiceMap{}
	self.factory = factory
	self.services = make(map[string]*ServiceSlot)
	self.changes = make(chan ServiceConfig)

	go func() {
		//		foundKeys := make(map[string]bool)

		for config := range self.changes {
			key := config.Key()
			slot := self.getSlot(key)
			if slot == nil {
				continue
			}

			slot.channel <- config
		}
	}()

	return self
}

func (self *ServiceMap) Channel() chan<- ServiceConfig {
	return self.changes
}

func (self *ServiceMap) getSlot(key string) *ServiceSlot {
	self.mutex.Lock()
	defer self.mutex.Unlock()

	slot := self.services[key]
	if slot == nil {
		channel := make(chan ServiceConfig)
		service := self.factory(key, channel)
		if service == nil {
			log.Warn("Could not create service for key: %v", key)
			return nil
		}

		slot = &ServiceSlot{}
		slot.service = service
		slot.channel = channel

		self.services[key] = slot
	}

	return slot

}

func (self *ServiceMap) keys() []string {
	self.mutex.Lock()
	defer self.mutex.Unlock()

	keys := make([]string, 0, len(self.services))
	for key, _ := range self.services {
		keys = append(keys, key)
	}
	return keys
}

//func (self *ServiceMap) Update(configs ServiceConfigs) errors.ErrorList {
//	e := errors.NewErrorList()
//
//	foundKeys := make(map[string]bool)
//
//	for _, config := range configs {
//		key := config.Key()
//		foundKeys[key] = true
//
//		service := self.services[key]
//		if service == nil {
//			service = self.factory(config)
//			if service == nil {
//				log.Warn("Could not create service for key: %v", key)
//				e.Add(fmt.Errorf("Could not create service for key: %v", key))
//			} else {
//				self.services[key] = service
//
//				e.AddAll(service.Start())
//			}
//		} else {
//			e.AddAll(service.Update(config))
//		}
//	}
//
//	for key, service := range self.services {
//		if !foundKeys[key] {
//			errs := service.Stop()
//
//			e.AddAll(errs)
//			if !errs.IsEmpty() {
//				log.Warn("Error stopping service: %v", service, errs)
//			} else {
//				delete(self.services, key)
//			}
//		}
//	}
//
//	return e
//}

func (self *ServiceMap) Stop() errors.ErrorList {
	log.Error("TODO: Implement!!!")
	return errors.NoErrors()
}

func (self *ServiceMap) remove(key string, tombstone ServiceConfig) {
	slot := self.getSlot(key)
	if slot == nil {
		log.Warn("Tried to remove service, but service not found: %v", key)
		return
	}

	self.mutex.Lock()
	defer self.mutex.Unlock()

	slot.channel <- tombstone
	close(slot.channel)

	delete(self.services, key)
}

func (self *ServiceMap) ReplaceConfigs(configs []ServiceConfig, fnTombstone func(string) ServiceConfig) {
	keys := make(map[string]bool)
	for _, key := range self.keys() {
		keys[key] = true
	}

	for _, config := range configs {
		key := config.Key()

		delete(keys, key)

		slot := self.getSlot(key)
		if slot == nil {
			continue
		}

		slot.channel <- config
	}

	for key, _ := range keys {
		tombstone := fnTombstone(key)

		self.remove(key, tombstone)
	}
}
<commit_msg>Pass config to service factory in SeviceMap<commit_after>package services

import (
	"github.com/justinsb/gova/assert"
	"github.com/justinsb/gova/errors"
	"github.com/justinsb/slf4g/log"

	"sync"
)

type ServiceMap struct {
	factory  fnServiceFactory
	services map[string]*ServiceSlot
	changes  chan ServiceConfig

	mutex sync.Mutex
}

type ServiceSlot struct {
	service interface{}
	channel chan<- ServiceConfig
}

type fnServiceFactory func(confg ServiceConfig, channel <-chan ServiceConfig) interface{}

func NewServiceMap(factory fnServiceFactory) *ServiceMap {
	self := &ServiceMap{}
	self.factory = factory
	self.services = make(map[string]*ServiceSlot)
	self.changes = make(chan ServiceConfig)

	go func() {
		//		foundKeys := make(map[string]bool)

		for config := range self.changes {
			slot := self.getSlot(config)
			if slot == nil {
				continue
			}

			slot.channel <- config
		}
	}()

	return self
}

func (self *ServiceMap) Channel() chan<- ServiceConfig {
	return self.changes
}

func (self *ServiceMap) getSlot(config ServiceConfig) *ServiceSlot {
	key := config.Key()

	self.mutex.Lock()
	defer self.mutex.Unlock()

	slot := self.services[key]
	if slot == nil {
		channel := make(chan ServiceConfig)
		service := self.factory(config, channel)
		if service == nil {
			log.Warn("Could not create service for key: %v", key)
			return nil
		}

		slot = &ServiceSlot{}
		slot.service = service
		slot.channel = channel

		self.services[key] = slot
	}

	return slot

}

func (self *ServiceMap) keys() []string {
	self.mutex.Lock()
	defer self.mutex.Unlock()

	keys := make([]string, 0, len(self.services))
	for key, _ := range self.services {
		keys = append(keys, key)
	}
	return keys
}

//func (self *ServiceMap) Update(configs ServiceConfigs) errors.ErrorList {
//	e := errors.NewErrorList()
//
//	foundKeys := make(map[string]bool)
//
//	for _, config := range configs {
//		key := config.Key()
//		foundKeys[key] = true
//
//		service := self.services[key]
//		if service == nil {
//			service = self.factory(config)
//			if service == nil {
//				log.Warn("Could not create service for key: %v", key)
//				e.Add(fmt.Errorf("Could not create service for key: %v", key))
//			} else {
//				self.services[key] = service
//
//				e.AddAll(service.Start())
//			}
//		} else {
//			e.AddAll(service.Update(config))
//		}
//	}
//
//	for key, service := range self.services {
//		if !foundKeys[key] {
//			errs := service.Stop()
//
//			e.AddAll(errs)
//			if !errs.IsEmpty() {
//				log.Warn("Error stopping service: %v", service, errs)
//			} else {
//				delete(self.services, key)
//			}
//		}
//	}
//
//	return e
//}

func (self *ServiceMap) Stop() errors.ErrorList {
	log.Error("TODO: Implement!!!")
	return errors.NoErrors()
}

func (self *ServiceMap) remove(tombstone ServiceConfig) {
	key := tombstone.Key()

	slot := self.getSlot(tombstone)
	if slot == nil {
		log.Warn("Tried to remove service, but service not found: %v", key)
		return
	}

	self.mutex.Lock()
	defer self.mutex.Unlock()

	slot.channel <- tombstone
	close(slot.channel)

	delete(self.services, key)
}

func (self *ServiceMap) ReplaceConfigs(configs []ServiceConfig, fnTombstone func(string) ServiceConfig) {
	keys := make(map[string]bool)
	for _, key := range self.keys() {
		keys[key] = true
	}

	for _, config := range configs {
		key := config.Key()

		keys[key] = false

		slot := self.getSlot(config)
		if slot == nil {
			continue
		}

		slot.channel <- config
	}

	for key, v := range keys {
		if !v {
			continue
		}

		tombstone := fnTombstone(key)

		assert.That(key == tombstone.Key())

		self.remove(tombstone)
	}
}
<|endoftext|>