"use strict";(self.webpackChunkdocs_openc3_com=self.webpackChunkdocs_openc3_com||[]).push([["5162"],{8351:function(e,n,i){i.r(n),i.d(n,{default:()=>h,frontMatter:()=>s,metadata:()=>r,assets:()=>a,toc:()=>l,contentTitle:()=>c});var r=JSON.parse('{"id":"guides/exposing_microservices","title":"Exposing Microservices","description":"Provide external accessibility to microservices","source":"@site/docs/guides/exposing_microservices.md","sourceDirName":"guides","slug":"/guides/exposing_microservices","permalink":"/tools/staticdocs/docs/guides/exposing_microservices","draft":false,"unlisted":false,"editUrl":"https://github.com/OpenC3/cosmos/tree/main/docs.openc3.com/docs/guides/exposing_microservices.md","tags":[],"version":"current","frontMatter":{"title":"Exposing Microservices","description":"Provide external accessibility to microservices","sidebar_custom_props":{"myEmoji":"\uD83D\uDEAA"}},"sidebar":"defaultSidebar","previous":{"title":"Custom Widgets","permalink":"/tools/staticdocs/docs/guides/custom-widgets"},"next":{"title":"Little Endian Bitfields","permalink":"/tools/staticdocs/docs/guides/little-endian-bitfields"}}'),t=i("2322"),o=i("2840");let s={title:"Exposing Microservices",description:"Provide external accessibility to microservices",sidebar_custom_props:{myEmoji:"\uD83D\uDEAA"}},c=void 0,a={},l=[{value:"Expose microservices using the PORT and ROUTE_PREFIX keywords",id:"expose-microservices-using-the-port-and-route_prefix-keywords",level:2},{value:"Connecting to microservices from a different INTERFACE in plugin.txt",id:"connecting-to-microservices-from-a-different-interface-in-plugintxt",level:2}];function d(e){let n={a:"a",admonition:"admonition",code:"code",h2:"h2",li:"li",p:"p",pre:"pre",ul:"ul",...(0,o.a)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.p,{children:"COSMOS provides a simple method to add new APIs and make custom microservices and interfaces accessible to the network."}),"\n",(0,t.jsx)(n.admonition,{title:"Make sure anything you expose is secure",type:"warning",children:(0,t.jsx)(n.p,{children:"Make sure that any new apis you expose check for user credentials and authorize actions appropriately."})}),"\n",(0,t.jsx)(n.h2,{id:"expose-microservices-using-the-port-and-route_prefix-keywords",children:"Expose microservices using the PORT and ROUTE_PREFIX keywords"}),"\n",(0,t.jsxs)(n.p,{children:["In your plugin.txt file, both ",(0,t.jsx)(n.a,{href:"../configuration/plugins#interface-1",children:"INTERFACE"})," and ",(0,t.jsx)(n.a,{href:"../configuration/plugins#microservice-1",children:"MICROSERVICE"})," support the keywords ",(0,t.jsx)(n.a,{href:"../configuration/plugins#port-1",children:"PORT"})," and ",(0,t.jsx)(n.a,{href:"../configuration/plugins#route_prefix-1",children:"ROUTE_PREFIX"}),"."]}),"\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.a,{href:"../configuration/plugins#port-1",children:"PORT"})," is used to declare the port(s) that your microservice is listening for connections on. This is used in combination with ",(0,t.jsx)(n.a,{href:"../configuration/plugins#route_prefix-1",children:"ROUTE_PREFIX"})," to create a dynamic traefik route to your microservice."]}),"\n",(0,t.jsx)(n.p,{children:"The following code is used internally to let traefik know where to connect to your microservice internally:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-ruby",children:"if ENV['OPENC3_OPERATOR_HOSTNAME']\n url = \"http://#{ENV['OPENC3_OPERATOR_HOSTNAME']}:#{port}\"\nelse\n if ENV['KUBERNETES_SERVICE_HOST']\n url = \"http://#{microservice_name.downcase.gsub('__', '-').gsub('_', '-')}-service:#{port}\"\n else\n url = \"http://openc3-operator:#{port}\"\n end\nend\n"})}),"\n",(0,t.jsx)(n.p,{children:"Note that this is the internal route to your microservice. Determining this route checks two different environment variables."}),"\n",(0,t.jsx)(n.p,{children:'OPENC3_OPERATOR_HOSTNAME is used to override the default service name for our regular docker compose operator of "openc3-operator". Usually this is not set.'}),"\n",(0,t.jsx)(n.p,{children:"In OpenC3 Enterprise, KUBERNETES_SERVICE_HOST is used to detect if we are running in a Kubernetes environment (it will be set by Kubernetes), in which case the service is expected to have a Kubernetes service named scope-user-microservicename-service. For example, if you are using the DEFAULT scope and have a microservice named MYMICROSERVICE the service would be found at the hostname: default-user-mymicroservice-service. Double underscores or single underscores are replaced by a dash and the name is all lower case."}),"\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.a,{href:"../configuration/plugins#route_prefix-1",children:"ROUTE_PREFIX"})," is used to define the external route. The external route will take the form of http(s)://YOURCOSMOSDOMAIN",":PORT","/ROUTE_PREFIX. So for example, if you set the ",(0,t.jsx)(n.a,{href:"../configuration/plugins#route_prefix-1",children:"ROUTE_PREFIX"})," to /mymicroservice then on a default local installation, it could be reached at ",(0,t.jsx)(n.code,{children:"http://localhost:2900/mymicroservice"}),". The ",(0,t.jsx)(n.code,{children:"http://localhost:2900"})," part should be substituted by whatever domain you are accessing COSMOS at."]}),"\n",(0,t.jsxs)(n.p,{children:["Here is a snippet of code showing ",(0,t.jsx)(n.a,{href:"../configuration/plugins#port-1",children:"PORT"})," and ",(0,t.jsx)(n.a,{href:"../configuration/plugins#route_prefix-1",children:"ROUTE_PREFIX"})," in use within a plugin.txt file:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"VARIABLE cfdp_microservice_name CFDP\nVARIABLE cfdp_route_prefix /cfdp\nVARIABLE cfdp_port 2905\n\nMICROSERVICE CFDP <%= cfdp_microservice_name %>\n WORK_DIR .\n ROUTE_PREFIX <%= cfdp_route_prefix %>\n PORT <%= cfdp_port %>\n"})}),"\n",(0,t.jsx)(n.p,{children:"Leaving the variables at their default values the following will occur:"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["The microservice will be exposed internally to Docker (Open Source or Enterprise) at: ",(0,t.jsx)(n.code,{children:"http://openc3-operator:2905"})]}),"\n",(0,t.jsxs)(n.li,{children:["The microservice will be exposed internally to Kubernetes (Enterprise) at: ",(0,t.jsx)(n.code,{children:"http://default-user-cfdp-service:2905"})]}),"\n",(0,t.jsxs)(n.li,{children:["The microservice will be exposed externally to the network at: ",(0,t.jsx)(n.code,{children:"http://localhost:2900/cfdp"})]}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["The same can be done for ",(0,t.jsx)(n.a,{href:"../configuration/plugins#interface-1",children:"INTERFACE"})," but note that the Kubernetes service name will use the microservice name of the interface which takes the form of ",(0,t.jsx)(n.code,{children:"SCOPE__INTERFACE__INTERFACENAME"}),"."]}),"\n",(0,t.jsxs)(n.p,{children:["Here is an example using ",(0,t.jsx)(n.a,{href:"../configuration/plugins#port",children:"PORT"})," and ",(0,t.jsx)(n.a,{href:"../configuration/plugins#route_prefix",children:"ROUTE_PREFIX"})," with ",(0,t.jsx)(n.a,{href:"../configuration/plugins#interface-1",children:"INTERFACE"}),":"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"VARIABLE my_interface_name MY_INT\nVARIABLE my_route_prefix /myint\nVARIABLE my_port 2910\n\nINTERFACE <%= my_interface_name %> http_server_interface.rb <%= my_port %>\n ROUTE_PREFIX <%= my_route_prefix %>\n PORT <%= my_port %>\n"})}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["The interface will be exposed internally to Docker (Open Source or Enterprise) at: ",(0,t.jsx)(n.code,{children:"http://openc3-operator:2910"})]}),"\n",(0,t.jsxs)(n.li,{children:["The interface will be exposed internally to Kubernetes (Enterprise) at: ",(0,t.jsx)(n.code,{children:"http://default-interface-my-int-service:2905"})]}),"\n",(0,t.jsxs)(n.li,{children:["The interface will be exposed externally to the network at: ",(0,t.jsx)(n.code,{children:"http://localhost:2900/myint"})]}),"\n"]}),"\n",(0,t.jsx)(n.admonition,{title:"Sharded Operator on Kubernetes (Enterprise)",type:"warning",children:(0,t.jsx)(n.p,{children:"The sharded operator is expected to be used on Kubernetes whenever the Kubernetes Operator is not used. Typically this will be because the user does not have permission to use the Kubernetes API directly to spawn containers which is required for use of the Kubernetes Operator. In this case, Kubernetes services will NOT be automatically created, and will have to be manually created by a user with permissions in Kubernetes, or through some other authorized method (like a custom framework dashboard or config file)."})}),"\n",(0,t.jsx)(n.h2,{id:"connecting-to-microservices-from-a-different-interface-in-plugintxt",children:"Connecting to microservices from a different INTERFACE in plugin.txt"}),"\n",(0,t.jsx)(n.p,{children:"Sometimes you might want to have an INTERFACE connect to a microservice you are running. For this case, only the PORT keyword is required on the INTERFACE or MICROSERVICE because we are only connecting internally and ROUTE_PREFIX isn't used."}),"\n",(0,t.jsx)(n.p,{children:"The following code taken from our demo plugin provides an example of how to calculate the correct hostname across both Open Source and Enterprise versions of COSMOS in a plugin.txt file:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:" <% example_host = ENV['KUBERNETES_SERVICE_HOST'] ? \"#{scope}-user-#{example_microservice_name.downcase.gsub('__', '-').gsub('_', '-')}-service\" : \"openc3-operator\" %>\n INTERFACE <%= example_int_name %> example_interface.rb <%= example_host %> <%= example_port %>\n MAP_TARGET <%= example_target_name %>\n"})}),"\n",(0,t.jsx)(n.p,{children:"Note that the above code does not handle the OPENC3_OPERATOR_HOSTNAME environment variable which might change the default name of openc3-operator. Update as needed."})]})}function h(e={}){let{wrapper:n}={...(0,o.a)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(d,{...e})}):d(e)}},2840:function(e,n,i){i.d(n,{Z:function(){return c},a:function(){return s}});var r=i(2784);let t={},o=r.createContext(t);function s(e){let n=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:s(e.components),r.createElement(o.Provider,{value:n},e.children)}}}]);