diff --git a/.github/actions/ci/action.yml b/.github/actions/ci/action.yml index f8f2e5a..df3042d 100644 --- a/.github/actions/ci/action.yml +++ b/.github/actions/ci/action.yml @@ -55,7 +55,10 @@ runs: - name: Run hello-lua-server example if: ${{ !contains(inputs.lua-version, 'jit') }} shell: bash - run: ./scripts/interpreter.sh ./examples/hello-lua-server/hello.lua + run: | + cd ./examples/hello-lua-server + lua hello.lua | tee output.txt + grep -F "is false for this user" output.txt || (echo "Expected false evaluation!" && exit 1) env: LD_SDK_KEY: "fake-sdk-key" # Needed because boost isn't installed in default system paths, which is diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5534b2c..35287ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,22 +42,26 @@ jobs: rockspec: ${{ matrix.package }} - hello-haproxy: + examples: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + name: ["hello-haproxy", "hello-nginx"] env: - LD_SDK_KEY: "foo" + LD_SDK_KEY: foo steps: - uses: actions/checkout@v4 - - name: Build hello-haproxy image + - name: Build ${{ matrix.name }} image run: | - docker build -t launchdarkly:hello-haproxy -f ./examples/hello-haproxy/Dockerfile . - - name: Run hello-haproxy container in background + docker build -t launchdarkly:${{ matrix.name }} -f ./examples/${{ matrix.name }}/Dockerfile . + - name: Run ${{ matrix.name }} container in background run: | - docker run -dit --rm --name hello-haproxy -p 8123:8123 --env LD_SDK_KEY="$LD_SDK_KEY" launchdarkly:hello-haproxy + docker run -dit --rm --name ${{ matrix.name }} -p 8123:80 --env LD_SDK_KEY="$LD_SDK_KEY" launchdarkly:${{ matrix.name }} - name: Evaluate feature flag run: | curl --retry 5 --retry-all-errors --retry-delay 1 -s -v http://localhost:8123 | tee response.txt - grep -F "Feature flag is false for this user" response.txt || (echo "Expected false evaluation!" && exit 1) - - name: Stop hello-haproxy container + grep -F "is false for this user" response.txt || (echo "Expected false evaluation!" && exit 1) + - name: Stop ${{ matrix.name }} container run: | - docker stop hello-haproxy + docker stop ${{ matrix.name }} diff --git a/README.md b/README.md index 4adf871..b5a16a7 100644 --- a/README.md +++ b/README.md @@ -52,10 +52,11 @@ getting started with using the SDK. The following examples are available in addition to the usage guide linked above: -| Example | Purpose | -|-------------------------------------------------|-----------------------------------------------------------------------------------------------| -| [hello-lua-server](./examples/hello-lua-server) | Demonstrates basic example of Lua SDK usage from the command line. | -| [hello-haproxy](./examples/hello-haproxy) | Demonstrates usage of the Lua SDK as a [HAproxy](https://www.haproxy.org/) module via Docker. | +| Example | Purpose | +|-------------------------------------------------|---------------------------------------------------------------------------------------------------------------------| +| [hello-lua-server](./examples/hello-lua-server) | Demonstrates basic example of Lua SDK usage from the command line. | +| [hello-haproxy](./examples/hello-haproxy) | Demonstrates usage of the Lua SDK as a [HAproxy](https://www.haproxy.org/) module via Docker. | +| [hello-nginx](./examples/hello-nginx) | Demonstrates usage of the Lua SDK as a [nginx OpenResty](https://github.com/openresty/openresty) module via Docker. | Before you start using the SDK, you'll need to install it. diff --git a/examples/env-helper/get_from_env_or_default.lua b/examples/env-helper/get_from_env_or_default.lua new file mode 100644 index 0000000..7c0efcd --- /dev/null +++ b/examples/env-helper/get_from_env_or_default.lua @@ -0,0 +1,17 @@ +-- This is a helper function to get a value from an existing variable, if not empty or nil, or otherwise +-- from an environment variable. +function get_from_env_or_default(env_variable_name, local_variable) + if local_variable ~= nil and local_variable ~= "" then + return local_variable + end + + local env_var = os.getenv(env_variable_name) + if env_var ~= nil and env_var ~= "" then + return env_var + end + + return "" +end + + +return get_from_env_or_default diff --git a/examples/hello-haproxy/Dockerfile b/examples/hello-haproxy/Dockerfile index 99bdb13..83cc4c8 100644 --- a/examples/hello-haproxy/Dockerfile +++ b/examples/hello-haproxy/Dockerfile @@ -21,6 +21,7 @@ RUN curl https://github.com/launchdarkly/cpp-sdks/releases/download/launchdarkly COPY . . COPY ./examples/hello-haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg COPY ./examples/hello-haproxy/service.lua /service.lua +COPY ./examples/env-helper/get_from_env_or_default.lua /get_from_env_or_default.lua RUN luarocks make launchdarkly-server-sdk-"${VERSION}"-0.rockspec LD_DIR=./cpp-sdk/build-dynamic/release diff --git a/examples/hello-haproxy/README.md b/examples/hello-haproxy/README.md index e96b4f1..1f7f7cb 100644 --- a/examples/hello-haproxy/README.md +++ b/examples/hello-haproxy/README.md @@ -4,13 +4,13 @@ We've built a minimal dockerized example of using the Lua SDK with [HAProxy](htt ## Build instructions -1. On the command line from the root of the repo, build the image from this directory with `docker build -t hello-haproxy -f ./examples/hello-haproxy/Dockerfile .`. +1. On the command line from the **root** of the repo, build the image from this directory with `docker build -t hello-haproxy -f ./examples/hello-haproxy/Dockerfile .`. 2. Run the demo with: - ``` - docker run -it --rm --name hello-haproxy -p 8123:8123 --env LD_SDK_KEY="your-sdk-key" --env LD_FLAG_KEY="my-boolean-flag" hello-haproxy``` + ```bash + docker run -it --rm --name hello-haproxy -p 8123:8123 --env LD_SDK_KEY="my-sdk-key" --env LD_FLAG_KEY="my-boolean-flag" hello-haproxy ``` 3. **Note:** the SDK key and flag key are passed with environment variables into the container. The `LD_FLAG_KEY` should be a boolean-type flag in your environment. 4. Open `localhost:8123` in your browser. Toggle the flag on to see a change in the page (refresh the page.) You should receive the message: -> Feature flag is for this user. +> Feature flag is for this user context diff --git a/examples/hello-haproxy/haproxy.cfg b/examples/hello-haproxy/haproxy.cfg index f04462a..f97f66b 100644 --- a/examples/hello-haproxy/haproxy.cfg +++ b/examples/hello-haproxy/haproxy.cfg @@ -8,7 +8,7 @@ defaults timeout server 30s frontend proxy - bind 0.0.0.0:8123 + bind 0.0.0.0:80 use_backend default_backend backend default_backend diff --git a/examples/hello-haproxy/service.lua b/examples/hello-haproxy/service.lua index ba3d74c..9b1e9ae 100644 --- a/examples/hello-haproxy/service.lua +++ b/examples/hello-haproxy/service.lua @@ -2,35 +2,19 @@ print "loaded" local os = require("os") local ld = require("launchdarkly_server_sdk") +local get_from_env_or_default = require("get_from_env_or_default") +-- Set MY_SDK_KEY to your LaunchDarkly SDK key. +local MY_SDK_KEY = "" --- Set YOUR_SDK_KEY to your LaunchDarkly SDK key. -local YOUR_SDK_KEY = "" - --- Set YOUR_FEATURE_KEY to the feature flag key you want to evaluate. -local YOUR_FEATURE_KEY = "my-boolean-flag" - - --- Allows the LaunchDarkly SDK key to be specified as an environment variable (LD_SDK_KEY) --- or locally in this example code (YOUR_SDK_KEY). -function get_key_from_env_or(name, existing_key) - if existing_key ~= nil and existing_key ~= "" then - core.Debug("Using LaunchDarkly SDK key from service.lua file") - return existing_key - end - - local env_key = os.getenv("LD_" .. name) - if env_key ~= nil and env_key ~= "" then - core.Debug("Using LaunchDarkly SDK key from LD_" .. name .. " environment variable") - return env_key - end - - core.log(core.crit, "LaunchDarkly SDK key not provided! SDK won't be initialized.") - return "" -end +-- Set MY_FLAG_KEY to the boolean-type feature flag key you want to evaluate. +local MY_FLAG_KEY = "my-boolean-flag" local config = {} -local client = ld.clientInit(get_key_from_env_or("SDK_KEY", YOUR_SDK_KEY), 1000, config) +local sdk_key = get_from_env_or_default("LD_SDK_KEY", MY_SDK_KEY) +local client = ld.clientInit(sdk_key, 1000, config) + +local flag_key = get_from_env_or_default("LD_FLAG_KEY", MY_FLAG_KEY) core.register_service("launchdarkly", "http", function(applet) applet:start_response() @@ -42,9 +26,9 @@ core.register_service("launchdarkly", "http", function(applet) } }) - if client:boolVariation(user, get_key_from_env_or("FLAG_KEY", YOUR_FEATURE_KEY), false) then - applet:send("

Feature flag is true for this user

") + if client:boolVariation(user, flag_key, false) then + applet:send("

Feature flag " .. flag_key .. " is true for this user context

") else - applet:send("

Feature flag is false for this user

") + applet:send("

Feature flag " .. flag_key .. " is false for this user context

") end end) diff --git a/examples/hello-lua-server/README.md b/examples/hello-lua-server/README.md new file mode 100644 index 0000000..265ff98 --- /dev/null +++ b/examples/hello-lua-server/README.md @@ -0,0 +1,16 @@ +# LaunchDarkly Lua server-side SDK basic example + +First, modify `hello.lua` to inject your SDK key and boolean-type feature flag key. + +Then, use the Lua interpreter to run `hello.lua`: +```bash +lua hello.lua +``` + +If you'd rather use environment variables to specify the SDK key or flag key, set `LD_SDK_KEY` or `LD_FLAG_KEY`. +```bash +LD_SDK_KEY=my-sdk-key LD_FLAG_KEY=my-boolean-flag lua hello.lua +``` + +The program should output: +> Feature flag is for this user context diff --git a/examples/hello-lua-server/hello.lua b/examples/hello-lua-server/hello.lua index 32f5f93..9442947 100644 --- a/examples/hello-lua-server/hello.lua +++ b/examples/hello-lua-server/hello.lua @@ -1,30 +1,17 @@ local ld = require("launchdarkly_server_sdk") +local get_from_env_or_default = dofile("../env-helper/get_from_env_or_default.lua") --- Allows the LaunchDarkly SDK key to be specified as an environment variable (LD_SDK_KEY) --- or locally in this example code (YOUR_SDK_KEY). -function get_key_from_env_or(existing_key) - if existing_key ~= "" then - return existing_key - end +-- Set MY_SDK_KEY to your LaunchDarkly SDK key. +local MY_SDK_KEY = "" - local env_key = os.getenv("LD_SDK_KEY") - if env_key ~= "" then - return env_key - end - - error("No SDK key specified (use LD_SDK_KEY environment variable or set local YOUR_SDK_KEY)") -end - --- Set YOUR_SDK_KEY to your LaunchDarkly SDK key. -local YOUR_SDK_KEY = "" - --- Set YOUR_FEATURE_KEY to the feature flag key you want to evaluate. -local YOUR_FEATURE_KEY = "my-boolean-flag" +-- Set MY_FLAG_KEY to the boolean-type feature flag key you want to evaluate. +local MY_FLAG_KEY = "my-boolean-flag" local config = {} -local client = ld.clientInit(get_key_from_env_or(YOUR_SDK_KEY), 1000, config) +local sdk_key = get_from_env_or_default("LD_SDK_KEY", MY_SDK_KEY) +local client = ld.clientInit(sdk_key, 1000, config) local user = ld.makeContext({ user = { @@ -33,5 +20,6 @@ local user = ld.makeContext({ } }) -local value = client:boolVariation(user, YOUR_FEATURE_KEY, false) -print("feature flag "..YOUR_FEATURE_KEY.." is "..tostring(value).." for this user") +local flag_key = get_from_env_or_default("LD_FLAG_KEY", MY_FLAG_KEY) +local value = client:boolVariation(user, flag_key, false) +print("Feature flag ".. flag_key .." is "..tostring(value).." for this user context") diff --git a/examples/hello-nginx/Dockerfile b/examples/hello-nginx/Dockerfile new file mode 100644 index 0000000..1fc2305 --- /dev/null +++ b/examples/hello-nginx/Dockerfile @@ -0,0 +1,56 @@ +FROM openresty/openresty:jammy + +# {{ x-release-please-start-version }} +ARG VERSION=2.0.6 +# {{ x-release-please-end }} + +RUN apt-get update && apt-get install -y \ + git netbase curl libssl-dev openssl libssl3 openssl apt-transport-https ca-certificates \ + software-properties-common \ + cmake ninja-build + +RUN add-apt-repository ppa:mhier/libboost-latest && \ + apt-get update && \ + apt-get install -y boost1.81 + + +RUN mkdir cpp-sdk-libs +RUN git clone --branch launchdarkly-cpp-server-v3.3.1 https://github.com/launchdarkly/cpp-sdks.git && \ + cd cpp-sdks && \ + mkdir build-dynamic && \ + cd build-dynamic && \ + cmake -GNinja \ + -DLD_BUILD_EXAMPLES=OFF \ + -DBUILD_TESTING=OFF \ + -DLD_BUILD_SHARED_LIBS=ON \ + -DLD_DYNAMIC_LINK_OPENSSL=ON .. && \ + cmake --build . --target launchdarkly-cpp-server && \ + cmake --install . --prefix=../../cpp-sdk-libs + +RUN mkdir -p /usr/local/openresty/nginx/scripts + +COPY . . +COPY ./examples/hello-nginx/nginx.conf /usr/local/openresty/nginx/conf/nginx.conf +COPY ./examples/hello-nginx/shared.lua /usr/local/openresty/nginx/scripts/ +COPY ./examples/env-helper/get_from_env_or_default.lua /usr/local/openresty/nginx/scripts/ + + +RUN luarocks make launchdarkly-server-sdk-"${VERSION}"-0.rockspec LD_DIR=./cpp-sdk-libs && \ + cp launchdarkly_server_sdk.so /usr/local/openresty/lualib/ + +# The strategy for this Docker example is to download the C++ SDK release artifacts and use those instead of compiling +# from source. This is for example/CI purposes only; generally it's better to build from source to ensure all libraries +# are compatible. +# +# Since we require a newer version of boost than is available in Ubuntu 22.04, we grab it from a PPA (mhier/libboost-latest). +# +# The SDK dynamic libs expect the boost libs to follow a specific naming convention, which isn't what +# the libraries from the PPA follow ('-mt' suffix is added to indicate the libraries are built with multithreading support enabled.) +# +# It's not 100% clear if these libraries are multithread enabled (build logs in the PPA seem to indicate it), +# but even so, the C++ SDK is single-threaded. +# +# To workaround, add symlinks with the expected names. +RUN cd /usr/lib/x86_64-linux-gnu && \ + ln -s libboost_json.so.1.81.0 libboost_json-mt-x64.so.1.81.0 && \ + ln -s libboost_url.so.1.81.0 libboost_url-mt-x64.so.1.81.0 diff --git a/examples/hello-nginx/README.md b/examples/hello-nginx/README.md new file mode 100644 index 0000000..e66134d --- /dev/null +++ b/examples/hello-nginx/README.md @@ -0,0 +1,16 @@ +# LaunchDarkly Lua server-side SDK NGINX example + +We've built a minimal dockerized example of using the Lua SDK with [OpenResty NGINX framework](https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/). For more comprehensive instructions, you can visit the [Using the Lua SDK with NGINX](https://docs.launchdarkly.com/guides/sdk/nginx) guide or the [Lua reference guide](https://docs.launchdarkly.com/sdk/server-side/lua). + +## Build instructions + +1. On the command line from the **root** of the repo, build the image from this directory with `docker build -t hello-nginx -f ./examples/hello-nginx/Dockerfile .`. +2. Run the demo with + ```bash + docker run --rm --name hello-nginx -p 8123:80 --env LD_SDK_KEY="my-sdk-key" --env LD_FLAG_KEY="my-boolean-flag" hello-nginx + ``` +3. **Note:** the SDK key and flag key are passed with environment variables into the container. The `LD_FLAG_KEY` should be a boolean-type flag in your environment. +4. Open `localhost:8123` in your browser. Toggle the flag on to see a change in the page (refresh the page.) + +You should receive the message: +> Feature flag is for this user context diff --git a/examples/hello-nginx/nginx.conf b/examples/hello-nginx/nginx.conf new file mode 100644 index 0000000..504d933 --- /dev/null +++ b/examples/hello-nginx/nginx.conf @@ -0,0 +1,48 @@ +events { + worker_connections 1024; +} + +env LD_SDK_KEY; +env LD_FLAG_KEY; + +http { + resolver 8.8.8.8; + + lua_package_path ";;/usr/local/openresty/nginx/scripts/?.lua;"; + + init_worker_by_lua_file scripts/shared.lua; + + server { + location / { + default_type text/html; + + content_by_lua_block { + local os = require("os") + local ld = require("launchdarkly_server_sdk") + local client = require("shared") + local get_from_env_or_default = require("get_from_env_or_default") + + + -- Set MY_FLAG_KEY to the feature flag key you want to evaluate. To specify the flag key as + -- an environment variable instead, set LD_FLAG_KEY using '--env LD_FLAG_KEY=my-boolean-flag-key' + -- as a 'docker run' argument. + + local MY_FLAG_KEY = "my-boolean-flag" + + local user = ld.makeContext({ + user = { + key = "example-user-key", + name = "Sandy" + } + }) + + local flag_key = get_from_env_or_default("LD_FLAG_KEY", MY_FLAG_KEY) + if client:boolVariation(user, flag_key, false) then + ngx.say("

Feature flag " .. flag_key .. " is true for this user context

") + else + ngx.say("

Feature flag " .. flag_key .. " is false for this user context

") + end + } + } + } +} diff --git a/examples/hello-nginx/shared.lua b/examples/hello-nginx/shared.lua new file mode 100644 index 0000000..08d8824 --- /dev/null +++ b/examples/hello-nginx/shared.lua @@ -0,0 +1,11 @@ +local ld = require("launchdarkly_server_sdk") +local os = require("os") +local get_from_env_or_default = require("get_from_env_or_default") + +-- Set MY_SDK_KEY to your LaunchDarkly SDK key. To specify the SDK key as an environment variable instead, +-- set LD_SDK_KEY using '--env LD_SDK_KEY=my-sdk-key' as a 'docker run' argument. +local MY_SDK_KEY = "" + +local config = {} + +return ld.clientInit(get_from_env_or_default("LD_SDK_KEY", MY_SDK_KEY), 1000, config)